diff options
| author | Scott Shawcroft <scott@tannewt.org> | 2017-09-22 18:05:51 -0700 |
|---|---|---|
| committer | Dan Halbert <halbert@halwitz.org> | 2017-09-22 21:05:51 -0400 |
| commit | 6839fff31303463d5c7942387fa498895edf1abf (patch) | |
| tree | f4c5b94c6534c8d5c65ea6b7209cd2d681508f5f /atmel-samd/asf4/samd51/hpl | |
| parent | 3ad01ddb04b160b74b4baee9fce043da17dbcef5 (diff) | |
Move to ASF4 and introduce SAMD51 support. (#258)3.0.0-alpha.0
* atmel-samd: Remove ASF3. This will break builds.
* atmel-samd: Add ASF4 for the SAMD21 and SAMD51.
* Introduce the supervisor concept to facilitate porting.
The supervisor is the code which runs individual MicroPython VMs. By
splitting it out we make it more consistent and easier to find.
This also adds very basic SAMD21 and SAMD51 support using the
supervisor. Only the REPL currently works.
This begins the work for #178.
Diffstat (limited to 'atmel-samd/asf4/samd51/hpl')
25 files changed, 8994 insertions, 0 deletions
diff --git a/atmel-samd/asf4/samd51/hpl/adc/hpl_adc.c b/atmel-samd/asf4/samd51/hpl/adc/hpl_adc.c new file mode 100644 index 000000000..89ea5f353 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/adc/hpl_adc.c @@ -0,0 +1,787 @@ + +/** + * \file + * + * \brief SAM Analog Digital Converter + * + * Copyright (C) 2015-2017 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_adc_async.h> +#include <hpl_adc_dma.h> +#include <hpl_adc_sync.h> +#include <utils_assert.h> +#include <utils_repeat_macro.h> +#include <hpl_adc_config.h> + +#ifndef CONF_ADC_0_ENABLE +#define CONF_ADC_0_ENABLE 0 +#endif +#ifndef CONF_ADC_1_ENABLE +#define CONF_ADC_1_ENABLE 0 +#endif + +/** + * \brief Macro is used to fill ADC configuration structure based on its number + * + * \param[in] n The number of structures + */ +#define ADC_CONFIGURATION(n) \ + { \ + (n), (CONF_ADC_##n##_RUNSTDBY << ADC_CTRLA_RUNSTDBY_Pos) | (CONF_ADC_##n##_ONDEMAND << ADC_CTRLA_ONDEMAND_Pos) \ + | ADC_CTRLA_PRESCALER(CONF_ADC_##n##_PRESCALER), \ + ADC_CTRLB_RESSEL(CONF_ADC_##n##_RESSEL) | (CONF_ADC_##n##_CORREN << ADC_CTRLB_CORREN_Pos) \ + | (CONF_ADC_##n##_FREERUN << ADC_CTRLB_FREERUN_Pos) \ + | (CONF_ADC_##n##_LEFTADJ << ADC_CTRLB_LEFTADJ_Pos) | ADC_CTRLB_WINMODE(CONF_ADC_##n##_WINMODE), \ + (CONF_ADC_##n##_REFCOMP << ADC_REFCTRL_REFCOMP_Pos) | ADC_REFCTRL_REFSEL(CONF_ADC_##n##_REFSEL), \ + (CONF_ADC_##n##_WINMONEO << ADC_EVCTRL_WINMONEO_Pos) \ + | (CONF_ADC_##n##_RESRDYEO << ADC_EVCTRL_RESRDYEO_Pos) \ + | (CONF_ADC_##n##_STARTINV << ADC_EVCTRL_STARTINV_Pos) \ + | (CONF_ADC_##n##_FLUSHINV << ADC_EVCTRL_FLUSHINV_Pos) \ + | (CONF_ADC_##n##_STARTEI << ADC_EVCTRL_STARTEI_Pos) \ + | (CONF_ADC_##n##_FLUSHEI << ADC_EVCTRL_FLUSHEI_Pos), \ + (CONF_ADC_##n##_DIFFMODE << ADC_INPUTCTRL_DIFFMODE_Pos) | ADC_INPUTCTRL_MUXNEG(CONF_ADC_##n##_MUXNEG) \ + | ADC_INPUTCTRL_MUXPOS(CONF_ADC_##n##_MUXPOS), \ + ADC_AVGCTRL_ADJRES(CONF_ADC_##n##_ADJRES) | ADC_AVGCTRL_SAMPLENUM(CONF_ADC_##n##_SAMPLENUM), \ + (CONF_ADC_##n##_OFFCOMP << ADC_SAMPCTRL_OFFCOMP_Pos) | ADC_SAMPCTRL_SAMPLEN(CONF_ADC_##n##_SAMPLEN), \ + ADC_WINLT_WINLT(CONF_ADC_##n##_WINLT), ADC_WINUT_WINUT(CONF_ADC_##n##_WINUT), \ + ADC_GAINCORR_GAINCORR(CONF_ADC_##n##_GAINCORR), ADC_OFFSETCORR_OFFSETCORR(CONF_ADC_##n##_OFFSETCORR), \ + CONF_ADC_##n##_DBGRUN << ADC_DBGCTRL_DBGRUN_Pos, \ + } + +/** + * \brief ADC configuration + */ +struct adc_configuration { + uint8_t number; + hri_adc_ctrla_reg_t ctrl_a; + hri_adc_ctrlb_reg_t ctrl_b; + hri_adc_refctrl_reg_t ref_ctrl; + hri_adc_evctrl_reg_t ev_ctrl; + hri_adc_inputctrl_reg_t input_ctrl; + hri_adc_avgctrl_reg_t avg_ctrl; + hri_adc_sampctrl_reg_t samp_ctrl; + hri_adc_winlt_reg_t win_lt; + hri_adc_winut_reg_t win_ut; + hri_adc_gaincorr_reg_t gain_corr; + hri_adc_offsetcorr_reg_t offset_corr; + hri_adc_dbgctrl_reg_t dbg_ctrl; +}; + +#define ADC_AMOUNT (CONF_ADC_0_ENABLE + CONF_ADC_1_ENABLE) + +/** + * \brief Array of ADC configurations + */ +static const struct adc_configuration _adcs[] = { +#if CONF_ADC_0_ENABLE == 1 + ADC_CONFIGURATION(0), +#endif +#if CONF_ADC_1_ENABLE == 1 + ADC_CONFIGURATION(1), +#endif +}; + +static void _adc_set_reference_source(void *const hw, const adc_reference_t reference); + +/** + * \brief Retrieve ordinal number of the given adc hardware instance + */ +static uint8_t _adc_get_hardware_index(const void *const hw) +{ + return ((uint32_t)hw - (uint32_t)ADC0) >> 10; +} + +/** \brief Return the pointer to register settings of specific ADC + * \param[in] hw_addr The hardware register base address. + * \return Pointer to register settings of specific ADC. + */ +static uint8_t _adc_get_regs(const uint32_t hw_addr) +{ + uint8_t n = _adc_get_hardware_index((const void *)hw_addr); + uint8_t i; + + for (i = 0; i < sizeof(_adcs) / sizeof(struct adc_configuration); i++) { + if (_adcs[i].number == n) { + return i; + } + } + + ASSERT(false); + return 0; +} + +/** + * \brief Retrieve IRQ number for the given hardware instance + */ +static uint8_t _adc_get_irq_num(const struct _adc_async_device *const device) +{ + + return ADC0_0_IRQn + (_adc_get_hardware_index(device->hw) << 1); +} + +/** + * \brief Init irq param with the given afec hardware instance + */ +static void _adc_init_irq_param(const void *const hw, struct _adc_async_device *dev) +{ +} + +/** + * \brief Initialize ADC + * + * \param[in] hw The pointer to hardware instance + * \param[in] i The number of hardware instance + */ +static int32_t _adc_init(void *const hw, const uint8_t i) +{ + + hri_adc_wait_for_sync(hw, ADC_SYNCBUSY_SWRST); + if (hri_adc_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + hri_adc_set_CTRLA_SWRST_bit(hw); + hri_adc_wait_for_sync(hw, ADC_SYNCBUSY_SWRST); + + hri_adc_write_CTRLB_reg(hw, _adcs[i].ctrl_b); + hri_adc_write_REFCTRL_reg(hw, _adcs[i].ref_ctrl); + hri_adc_write_EVCTRL_reg(hw, _adcs[i].ev_ctrl); + hri_adc_write_INPUTCTRL_reg(hw, _adcs[i].input_ctrl); + hri_adc_write_AVGCTRL_reg(hw, _adcs[i].avg_ctrl); + hri_adc_write_SAMPCTRL_reg(hw, _adcs[i].samp_ctrl); + hri_adc_write_WINLT_reg(hw, _adcs[i].win_lt); + hri_adc_write_WINUT_reg(hw, _adcs[i].win_ut); + hri_adc_write_GAINCORR_reg(hw, _adcs[i].gain_corr); + hri_adc_write_OFFSETCORR_reg(hw, _adcs[i].offset_corr); + hri_adc_write_DBGCTRL_reg(hw, _adcs[i].dbg_ctrl); + hri_adc_write_CTRLA_reg(hw, _adcs[i].ctrl_a); + + return ERR_NONE; +} + +/** + * \brief De-initialize ADC + * + * \param[in] hw The pointer to hardware instance + */ +static inline void _adc_deinit(void *hw) +{ + hri_adc_clear_CTRLA_ENABLE_bit(hw); + hri_adc_set_CTRLA_SWRST_bit(hw); +} + +/** + * \brief Initialize ADC + */ +int32_t _adc_sync_init(struct _adc_sync_device *const device, void *const hw) +{ + ASSERT(device); + + device->hw = hw; + + return _adc_init(hw, _adc_get_regs((uint32_t)hw)); +} + +/** + * \brief Initialize ADC + */ +int32_t _adc_async_init(struct _adc_async_device *const device, void *const hw) +{ + int32_t init_status; + uint8_t i = _adc_get_regs((uint32_t)hw); + + ASSERT(device); + + init_status = _adc_init(hw, _adc_get_regs((uint32_t)hw)); + if (init_status) { + return init_status; + } + device->hw = hw; + _adc_init_irq_param(hw, device); + NVIC_DisableIRQ(_adc_get_irq_num(device) + 0); + NVIC_ClearPendingIRQ(_adc_get_irq_num(device) + 0); + NVIC_EnableIRQ(_adc_get_irq_num(device) + 0); + NVIC_DisableIRQ(_adc_get_irq_num(device) + 1); + NVIC_ClearPendingIRQ(_adc_get_irq_num(device) + 1); + NVIC_EnableIRQ(_adc_get_irq_num(device) + 1); + return ERR_NONE; +} + +/** + * \brief Initialize ADC + */ +int32_t _adc_dma_init(struct _adc_dma_device *const device, void *const hw) +{ + ASSERT(device); + + device->hw = hw; + + return _adc_init(hw, _adc_get_regs((uint32_t)hw)); +} + +/** + * \brief De-initialize ADC + */ +void _adc_sync_deinit(struct _adc_sync_device *const device) +{ + _adc_deinit(device->hw); +} + +/** + * \brief De-initialize ADC + */ +void _adc_async_deinit(struct _adc_async_device *const device) +{ + NVIC_DisableIRQ(_adc_get_irq_num(device)); + NVIC_ClearPendingIRQ(_adc_get_irq_num(device)); + + _adc_deinit(device->hw); +} + +/** + * \brief De-initialize ADC + */ +void _adc_dma_deinit(struct _adc_dma_device *const device) +{ + _adc_deinit(device->hw); +} + +/** + * \brief Enable ADC + */ +void _adc_sync_enable_channel(struct _adc_sync_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Enable ADC + */ +void _adc_async_enable_channel(struct _adc_async_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Enable ADC + */ +void _adc_dma_enable_channel(struct _adc_dma_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable ADC + */ +void _adc_sync_disable_channel(struct _adc_sync_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable ADC + */ +void _adc_async_disable_channel(struct _adc_async_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable ADC + */ +void _adc_dma_disable_channel(struct _adc_dma_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Return address of ADC DMA source + */ +uint32_t _adc_get_source_for_dma(struct _adc_dma_device *const device) +{ + return (uint32_t) & (((Adc *)(device->hw))->RESULT.reg); +} + +/** + * \brief Retrieve ADC conversion data size + */ +uint8_t _adc_sync_get_data_size(const struct _adc_sync_device *const device) +{ + return hri_adc_read_CTRLB_RESSEL_bf(device->hw) == ADC_CTRLB_RESSEL_8BIT_Val ? 1 : 2; +} + +/** + * \brief Retrieve ADC conversion data size + */ +uint8_t _adc_async_get_data_size(const struct _adc_async_device *const device) +{ + return hri_adc_read_CTRLB_RESSEL_bf(device->hw) == ADC_CTRLB_RESSEL_8BIT_Val ? 1 : 2; +} + +/** + * \brief Retrieve ADC conversion data size + */ +uint8_t _adc_dma_get_data_size(const struct _adc_dma_device *const device) +{ + return hri_adc_read_CTRLB_RESSEL_bf(device->hw) == ADC_CTRLB_RESSEL_8BIT_Val ? 1 : 2; +} + +/** + * \brief Check if conversion is done + */ +bool _adc_sync_is_channel_conversion_done(const struct _adc_sync_device *const device, const uint8_t channel) +{ + (void)channel; + + return hri_adc_get_interrupt_RESRDY_bit(device->hw); +} + +/** + * \brief Check if conversion is done + */ +bool _adc_async_is_channel_conversion_done(const struct _adc_async_device *const device, const uint8_t channel) +{ + (void)channel; + + return hri_adc_get_interrupt_RESRDY_bit(device->hw); +} + +/** + * \brief Check if conversion is done + */ +bool _adc_dma_is_conversion_done(const struct _adc_dma_device *const device) +{ + return hri_adc_get_interrupt_RESRDY_bit(device->hw); +} + +/** + * \brief Make conversion + */ +void _adc_sync_convert(struct _adc_sync_device *const device) +{ + hri_adc_set_SWTRIG_START_bit(device->hw); +} + +/** + * \brief Make conversion + */ +void _adc_async_convert(struct _adc_async_device *const device) +{ + hri_adc_set_SWTRIG_START_bit(device->hw); +} + +/** + * \brief Make conversion + */ +void _adc_dma_convert(struct _adc_dma_device *const device) +{ + hri_adc_set_SWTRIG_START_bit(device->hw); +} + +/** + * \brief Retrieve the conversion result + */ +uint16_t _adc_sync_read_channel_data(const struct _adc_sync_device *const device, const uint8_t channel) +{ + (void)channel; + + return hri_adc_read_RESULT_reg(device->hw); +} + +/** + * \brief Retrieve the conversion result + */ +uint16_t _adc_async_read_channel_data(const struct _adc_async_device *const device, const uint8_t channel) +{ + (void)channel; + + return hri_adc_read_RESULT_reg(device->hw); +} + +/** + * \brief Set reference source + */ +void _adc_sync_set_reference_source(struct _adc_sync_device *const device, const adc_reference_t reference) +{ + _adc_set_reference_source(device->hw, reference); +} + +/** + * \brief Set reference source + */ +void _adc_async_set_reference_source(struct _adc_async_device *const device, const adc_reference_t reference) +{ + _adc_set_reference_source(device->hw, reference); +} + +/** + * \brief Set reference source + */ +void _adc_dma_set_reference_source(struct _adc_dma_device *const device, const adc_reference_t reference) +{ + _adc_set_reference_source(device->hw, reference); +} + +/** + * \brief Set resolution + */ +void _adc_sync_set_resolution(struct _adc_sync_device *const device, const adc_resolution_t resolution) +{ + hri_adc_write_CTRLB_RESSEL_bf(device->hw, resolution); +} + +/** + * \brief Set resolution + */ +void _adc_async_set_resolution(struct _adc_async_device *const device, const adc_resolution_t resolution) +{ + hri_adc_write_CTRLB_RESSEL_bf(device->hw, resolution); +} + +/** + * \brief Set resolution + */ +void _adc_dma_set_resolution(struct _adc_dma_device *const device, const adc_resolution_t resolution) +{ + hri_adc_write_CTRLB_RESSEL_bf(device->hw, resolution); +} + +/** + * \brief Set channels input sources + */ +void _adc_sync_set_inputs(struct _adc_sync_device *const device, const adc_pos_input_t pos_input, + const adc_neg_input_t neg_input, const uint8_t channel) +{ + (void)channel; + + hri_adc_write_INPUTCTRL_MUXPOS_bf(device->hw, pos_input); + hri_adc_write_INPUTCTRL_MUXNEG_bf(device->hw, neg_input); +} + +/** + * \brief Set channels input sources + */ +void _adc_async_set_inputs(struct _adc_async_device *const device, const adc_pos_input_t pos_input, + const adc_neg_input_t neg_input, const uint8_t channel) +{ + (void)channel; + + hri_adc_write_INPUTCTRL_MUXPOS_bf(device->hw, pos_input); + hri_adc_write_INPUTCTRL_MUXNEG_bf(device->hw, neg_input); +} + +/** + * \brief Set channels input source + */ +void _adc_dma_set_inputs(struct _adc_dma_device *const device, const adc_pos_input_t pos_input, + const adc_neg_input_t neg_input, const uint8_t channel) +{ + (void)channel; + + hri_adc_write_INPUTCTRL_MUXPOS_bf(device->hw, pos_input); + hri_adc_write_INPUTCTRL_MUXNEG_bf(device->hw, neg_input); +} + +/** + * \brief Set thresholds + */ +void _adc_sync_set_thresholds(struct _adc_sync_device *const device, const adc_threshold_t low_threshold, + const adc_threshold_t up_threshold) +{ + hri_adc_write_WINLT_reg(device->hw, low_threshold); + hri_adc_write_WINUT_reg(device->hw, up_threshold); +} + +/** + * \brief Set thresholds + */ +void _adc_async_set_thresholds(struct _adc_async_device *const device, const adc_threshold_t low_threshold, + const adc_threshold_t up_threshold) +{ + hri_adc_write_WINLT_reg(device->hw, low_threshold); + hri_adc_write_WINUT_reg(device->hw, up_threshold); +} + +/** + * \brief Set thresholds + */ +void _adc_dma_set_thresholds(struct _adc_dma_device *const device, const adc_threshold_t low_threshold, + const adc_threshold_t up_threshold) +{ + hri_adc_write_WINLT_reg(device->hw, low_threshold); + hri_adc_write_WINUT_reg(device->hw, up_threshold); +} + +/** + * \brief Set gain + */ +void _adc_sync_set_channel_gain(struct _adc_sync_device *const device, const uint8_t channel, const adc_gain_t gain) +{ + (void)device, (void)channel, (void)gain; +} + +/** + * \brief Set gain + */ +void _adc_async_set_channel_gain(struct _adc_async_device *const device, const uint8_t channel, const adc_gain_t gain) +{ + (void)device, (void)channel, (void)gain; +} + +/** + * \brief Set gain + */ +void _adc_dma_set_channel_gain(struct _adc_dma_device *const device, const uint8_t channel, const adc_gain_t gain) +{ + (void)device, (void)channel, (void)gain; +} + +/** + * \brief Set conversion mode + */ +void _adc_sync_set_conversion_mode(struct _adc_sync_device *const device, const enum adc_conversion_mode mode) +{ + if (ADC_CONVERSION_MODE_FREERUN == mode) { + hri_adc_set_CTRLB_FREERUN_bit(device->hw); + } else { + hri_adc_clear_CTRLB_FREERUN_bit(device->hw); + } +} + +/** + * \brief Set conversion mode + */ +void _adc_async_set_conversion_mode(struct _adc_async_device *const device, const enum adc_conversion_mode mode) +{ + if (ADC_CONVERSION_MODE_FREERUN == mode) { + hri_adc_set_CTRLB_FREERUN_bit(device->hw); + } else { + hri_adc_clear_CTRLB_FREERUN_bit(device->hw); + } +} + +/** + * \brief Set conversion mode + */ +void _adc_dma_set_conversion_mode(struct _adc_dma_device *const device, const enum adc_conversion_mode mode) +{ + if (ADC_CONVERSION_MODE_FREERUN == mode) { + hri_adc_set_CTRLB_FREERUN_bit(device->hw); + } else { + hri_adc_clear_CTRLB_FREERUN_bit(device->hw); + } +} + +/** + * \brief Set differential mode + */ +void _adc_sync_set_channel_differential_mode(struct _adc_sync_device *const device, const uint8_t channel, + const enum adc_differential_mode mode) +{ + (void)channel; + + if (ADC_DIFFERENTIAL_MODE_DIFFERENTIAL == mode) { + hri_adc_set_INPUTCTRL_DIFFMODE_bit(device->hw); + } else { + hri_adc_clear_INPUTCTRL_DIFFMODE_bit(device->hw); + } +} + +/** + * \brief Set differential mode + */ +void _adc_async_set_channel_differential_mode(struct _adc_async_device *const device, const uint8_t channel, + const enum adc_differential_mode mode) +{ + (void)channel; + + if (ADC_DIFFERENTIAL_MODE_DIFFERENTIAL == mode) { + hri_adc_set_INPUTCTRL_DIFFMODE_bit(device->hw); + } else { + hri_adc_clear_INPUTCTRL_DIFFMODE_bit(device->hw); + } +} + +/** + * \brief Set differential mode + */ +void _adc_dma_set_channel_differential_mode(struct _adc_dma_device *const device, const uint8_t channel, + const enum adc_differential_mode mode) +{ + (void)channel; + + if (ADC_DIFFERENTIAL_MODE_DIFFERENTIAL == mode) { + hri_adc_set_INPUTCTRL_DIFFMODE_bit(device->hw); + } else { + hri_adc_clear_INPUTCTRL_DIFFMODE_bit(device->hw); + } +} + +/** + * \brief Set window mode + */ +void _adc_sync_set_window_mode(struct _adc_sync_device *const device, const adc_window_mode_t mode) +{ + hri_adc_write_CTRLB_WINMODE_bf(device->hw, mode); +} + +/** + * \brief Set window mode + */ +void _adc_async_set_window_mode(struct _adc_async_device *const device, const adc_window_mode_t mode) +{ + hri_adc_write_CTRLB_WINMODE_bf(device->hw, mode); +} + +/** + * \brief Set window mode + */ +void _adc_dma_set_window_mode(struct _adc_dma_device *const device, const adc_window_mode_t mode) +{ + hri_adc_write_CTRLB_WINMODE_bf(device->hw, mode); +} + +/** + * \brief Retrieve threshold state + */ +void _adc_sync_get_threshold_state(const struct _adc_sync_device *const device, adc_threshold_status_t *const state) +{ + *state = hri_adc_get_interrupt_WINMON_bit(device->hw); +} + +/** + * \brief Retrieve threshold state + */ +void _adc_async_get_threshold_state(const struct _adc_async_device *const device, adc_threshold_status_t *const state) +{ + *state = hri_adc_get_interrupt_WINMON_bit(device->hw); +} + +/** + * \brief Retrieve threshold state + */ +void _adc_dma_get_threshold_state(const struct _adc_dma_device *const device, adc_threshold_status_t *const state) +{ + *state = hri_adc_get_interrupt_WINMON_bit(device->hw); +} + +/** + * \brief Enable/disable ADC channel interrupt + */ +void _adc_async_set_irq_state(struct _adc_async_device *const device, const uint8_t channel, + const enum _adc_async_callback_type type, const bool state) +{ + (void)channel; + + void *const hw = device->hw; + + if (ADC_ASYNC_DEVICE_MONITOR_CB == type) { + hri_adc_write_INTEN_WINMON_bit(hw, state); + } else if (ADC_ASYNC_DEVICE_ERROR_CB == type) { + hri_adc_write_INTEN_OVERRUN_bit(hw, state); + } else if (ADC_ASYNC_DEVICE_CONVERT_CB == type) { + hri_adc_write_INTEN_RESRDY_bit(hw, state); + } +} + +/** + * \internal ADC interrupt handler + * + * \param[in] p The pointer to interrupt parameter + */ +static void _adc_interrupt_handler(struct _adc_async_device *device) +{ + void *const hw = device->hw; + + if (hri_adc_get_interrupt_RESRDY_bit(hw)) { + hri_adc_clear_interrupt_RESRDY_bit(hw); + device->adc_async_ch_cb.convert_done(device, 0, hri_adc_read_RESULT_reg(hw)); + } else if (hri_adc_get_interrupt_OVERRUN_bit(hw)) { + hri_adc_clear_interrupt_OVERRUN_bit(hw); + device->adc_async_cb.error_cb(device, 0); + } else if (hri_adc_get_interrupt_WINMON_bit(hw)) { + hri_adc_clear_interrupt_WINMON_bit(hw); + device->adc_async_cb.window_cb(device, 0); + } +} + +/** + * \brief Retrieve ADC sync helper functions + */ +void *_adc_get_adc_sync(void) +{ + return (void *)NULL; +} + +/** + * \brief Retrieve ADC async helper functions + */ +void *_adc_get_adc_async(void) +{ + return (void *)NULL; +} + +/** + * \brief Set ADC reference source + * + * \param[in] hw The pointer to hardware instance + * \param[in] reference The reference to set + */ +static void _adc_set_reference_source(void *const hw, const adc_reference_t reference) +{ + bool enabled = hri_adc_get_CTRLA_ENABLE_bit(hw); + + hri_adc_clear_CTRLA_ENABLE_bit(hw); + hri_adc_write_REFCTRL_REFSEL_bf(hw, reference); + + if (enabled) { + hri_adc_set_CTRLA_ENABLE_bit(hw); + } +} diff --git a/atmel-samd/asf4/samd51/hpl/adc/hpl_adc_base.h b/atmel-samd/asf4/samd51/hpl/adc/hpl_adc_base.h new file mode 100644 index 000000000..2f9a333c9 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/adc/hpl_adc_base.h @@ -0,0 +1,82 @@ +/** + * \file + * + * \brief ADC related functionality declaration. + * + * Copyright (C) 2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#ifndef _HPL_ADC_ADC_H_INCLUDED +#define _HPL_ADC_ADC_H_INCLUDED + +#include <hpl_adc_sync.h> +#include <hpl_adc_async.h> + +/** + * \addtogroup HPL ADC + * + * \section hpl_adc_rev Revision History + * - v1.0.0 Initial Release + * + *@{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name HPL functions + */ +//@{ + +/** + * \brief Retrieve ADC helper functions + * + * \return A pointer to set of ADC helper functions + */ +void *_adc_get_adc_sync(void); +void *_adc_get_adc_async(void); + +//@} + +#ifdef __cplusplus +} +#endif +/**@}*/ +#endif /* _HPL_USART_UART_H_INCLUDED */ diff --git a/atmel-samd/asf4/samd51/hpl/core/hpl_core_m4.c b/atmel-samd/asf4/samd51/hpl/core/hpl_core_m4.c new file mode 100644 index 000000000..cea8d7c3c --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/core/hpl_core_m4.c @@ -0,0 +1,213 @@ +/** + * \file + * + * \brief Core related functionality implementation. + * + * Copyright (C) 2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_core.h> +#include <hpl_irq.h> +#ifndef _UNIT_TEST_ +#include <utils.h> +#endif +#include <utils_assert.h> +#include <peripheral_clk_config.h> + +#ifndef CONF_CPU_FREQUENCY +#define CONF_CPU_FREQUENCY 1000000 +#endif + +#if CONF_CPU_FREQUENCY < 1000 +#define CPU_FREQ_POWER 3 +#elif CONF_CPU_FREQUENCY < 10000 +#define CPU_FREQ_POWER 4 +#elif CONF_CPU_FREQUENCY < 100000 +#define CPU_FREQ_POWER 5 +#elif CONF_CPU_FREQUENCY < 1000000 +#define CPU_FREQ_POWER 6 +#elif CONF_CPU_FREQUENCY < 10000000 +#define CPU_FREQ_POWER 7 +#elif CONF_CPU_FREQUENCY < 100000000 +#define CPU_FREQ_POWER 8 +#elif CONF_CPU_FREQUENCY < 1000000000 +#define CPU_FREQ_POWER 9 +#endif + +/** + * \brief The array of interrupt handlers + */ +struct _irq_descriptor *_irq_table[PERIPH_COUNT_IRQn]; + +/** + * \brief Reset MCU + */ +void _reset_mcu(void) +{ + NVIC_SystemReset(); +} + +/** + * \brief Put MCU to sleep + */ +void _go_to_sleep(void) +{ + __DSB(); + __WFI(); +} + +/** + * \brief Retrieve current IRQ number + */ +uint8_t _irq_get_current(void) +{ + return (uint8_t)__get_IPSR() - 16; +} + +/** + * \brief Disable the given IRQ + */ +void _irq_disable(uint8_t n) +{ + NVIC_DisableIRQ((IRQn_Type)n); +} + +/** + * \brief Set the given IRQ + */ +void _irq_set(uint8_t n) +{ + NVIC_SetPendingIRQ((IRQn_Type)n); +} + +/** + * \brief Clear the given IRQ + */ +void _irq_clear(uint8_t n) +{ + NVIC_ClearPendingIRQ((IRQn_Type)n); +} + +/** + * \brief Enable the given IRQ + */ +void _irq_enable(uint8_t n) +{ + NVIC_EnableIRQ((IRQn_Type)n); +} + +/** + * \brief Register IRQ handler + */ +void _irq_register(const uint8_t n, struct _irq_descriptor *const irq) +{ + ASSERT(n < PERIPH_COUNT_IRQn); + + _irq_table[n] = irq; +} + +/** + * \brief Default interrupt handler for unused IRQs. + */ +void Default_Handler(void) +{ + while (1) { + } +} + +/** + * \brief Retrieve the amount of cycles to delay for the given amount of us + */ +static inline uint32_t _get_cycles_for_us_internal(const uint16_t us, const uint32_t freq, const uint8_t power) +{ + switch (power) { + case 9: + return (us * (freq / 1000000) - 1) + 1; + case 8: + return (us * (freq / 100000) - 1) / 10 + 1; + case 7: + return (us * (freq / 10000) - 1) / 100 + 1; + case 6: + return (us * (freq / 1000) - 1) / 1000 + 1; + case 5: + return (us * (freq / 100) - 1) / 10000 + 1; + case 4: + return (us * (freq / 10) - 1) / 100000 + 1; + default: + return (us * freq - 1) / 1000000 + 1; + } +} + +/** + * \brief Retrieve the amount of cycles to delay for the given amount of us + */ +uint32_t _get_cycles_for_us(const uint16_t us) +{ + return _get_cycles_for_us_internal(us, CONF_CPU_FREQUENCY, CPU_FREQ_POWER); +} + +/** + * \brief Retrieve the amount of cycles to delay for the given amount of ms + */ +static inline uint32_t _get_cycles_for_ms_internal(const uint16_t ms, const uint32_t freq, const uint8_t power) +{ + switch (power) { + case 9: + return (ms * (freq / 1000000)) * 1000; + case 8: + return (ms * (freq / 100000)) * 100; + case 7: + return (ms * (freq / 10000)) * 10; + case 6: + return (ms * (freq / 1000)); + case 5: + return (ms * (freq / 100) - 1) / 10 + 1; + case 4: + return (ms * (freq / 10) - 1) / 100 + 1; + default: + return (ms * freq - 1) / 1000 + 1; + } +} + +/** + * \brief Retrieve the amount of cycles to delay for the given amount of ms + */ +uint32_t _get_cycles_for_ms(const uint16_t ms) +{ + return _get_cycles_for_ms_internal(ms, CONF_CPU_FREQUENCY, CPU_FREQ_POWER); +} diff --git a/atmel-samd/asf4/samd51/hpl/core/hpl_core_port.h b/atmel-samd/asf4/samd51/hpl/core/hpl_core_port.h new file mode 100644 index 000000000..b068af839 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/core/hpl_core_port.h @@ -0,0 +1,71 @@ +/** + * \file + * + * \brief Core related functionality implementation. + * + * Copyright (C) 2015 - 2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#ifndef _HPL_CORE_PORT_H_INCLUDED +#define _HPL_CORE_PORT_H_INCLUDED + +#include <peripheral_clk_config.h> + +/* It's possible to include this file in ARM ASM files (e.g., in FreeRTOS IAR + * portable implement, portasm.s -> FreeRTOSConfig.h -> hpl_core_port.h), + * there will be assembling errors. + * So the following things are not included for assembling. + */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) + +#ifndef _UNIT_TEST_ +#include <compiler.h> +#endif + +/** + * \brief Check if it's in ISR handling + * \return \c true if it's in ISR + */ +static inline bool _is_in_isr(void) +{ + return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk); +} + +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +#endif /* _HPL_CORE_PORT_H_INCLUDED */ diff --git a/atmel-samd/asf4/samd51/hpl/core/hpl_init.c b/atmel-samd/asf4/samd51/hpl/core/hpl_init.c new file mode 100644 index 000000000..87c277b2b --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/core/hpl_init.c @@ -0,0 +1,69 @@ +/** + * \file + * + * \brief HPL initialization related functionality implementation. + * + * Copyright (C) 2014-2017 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_gpio.h> +#include <hpl_init.h> +#include <hpl_mclk_config.h> + +#include <hpl_dma.h> +#include <hpl_dmac_config.h> + +/** + * \brief Initialize the hardware abstraction layer + */ +void _init_chip(void) +{ + + hri_nvmctrl_set_CTRLA_RWS_bf(NVMCTRL, CONF_NVM_WAIT_STATE); + + _osc32kctrl_init_sources(); + _oscctrl_init_sources(); + _mclk_init(); + _gclk_init_generators(); + _oscctrl_init_referenced_generators(); + +#if CONF_DMAC_ENABLE + hri_mclk_set_AHBMASK_DMAC_bit(MCLK); + _dma_init(); +#endif +} diff --git a/atmel-samd/asf4/samd51/hpl/dac/hpl_dac.c b/atmel-samd/asf4/samd51/hpl/dac/hpl_dac.c new file mode 100644 index 000000000..411f4f5e9 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/dac/hpl_dac.c @@ -0,0 +1,326 @@ + +/** +* \file +* +* \brief SAM Digital to Analog Converter +* +* Copyright (C) 2016 - 2017 Atmel Corporation. All rights reserved. +* +* \asf_license_start +* +* \page License +* +* 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. The name of Atmel may not be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* 4. This software may only be redistributed and used in connection with an +* Atmel microcontroller product. +* +* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE +* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMIT ED 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. +* +* \asf_license_stop +* +*/ + +#include <hpl_dac_config.h> +#include <utils_assert.h> +#include <hpl_dac_sync.h> +#include <hpl_dac_async.h> + +/** \conf INTERNAL */ +static int32_t _dac_init(void *const hw); +static inline void _dac_deinit(void *const hw); +static inline void _dac_enable_channel(void *const hw, const uint8_t ch); +static inline void _dac_disable_channel(void *const hw, const uint8_t ch); +static inline bool _dac_is_channel_enabled(void *const hw, const uint8_t ch); +/** \endcond */ + +/** + * \brief DAC configuration type + */ +struct dac_configuration { + hri_dac_ctrlb_reg_t ctrlb; + hri_dac_evctrl_reg_t ev_ctrl; + hri_dac_dacctrl_reg_t dac_ctrl0; + hri_dac_dacctrl_reg_t dac_ctrl1; + hri_dac_dbgctrl_reg_t dbg_ctrl; +}; + +/** + * \brief Array of DAC configurations + */ +static struct dac_configuration _dac + = {(CONF_DAC_DIFF << DAC_CTRLB_DIFF_Pos) | DAC_CTRLB_REFSEL(CONF_DAC_REFSEL), + (CONF_DAC_INVEI0 << DAC_EVCTRL_INVEI0_Pos) | (CONF_DAC_EMPTYEO0 << DAC_EVCTRL_EMPTYEO0_Pos) + | (CONF_DAC_STARTEI0 << DAC_EVCTRL_STARTEI0_Pos) + | (CONF_DAC_INVEI1 << DAC_EVCTRL_INVEI1_Pos) + | (CONF_DAC_EMPTYEO1 << DAC_EVCTRL_EMPTYEO1_Pos) + | (CONF_DAC_STARTEI1 << DAC_EVCTRL_STARTEI1_Pos), + (CONF_DAC0_LEFTADJ << DAC_DACCTRL_LEFTADJ_Pos) | DAC_DACCTRL_CCTRL(CONF_DAC0_CCTRL) + | (CONF_DAC0_RUNSTDBY << DAC_DACCTRL_RUNSTDBY_Pos) + | (CONF_DAC0_DITHER << DAC_DACCTRL_DITHER_Pos) + | (CONF_DAC0_REFRESH << DAC_DACCTRL_REFRESH_Pos), + (CONF_DAC1_LEFTADJ << DAC_DACCTRL_LEFTADJ_Pos) | DAC_DACCTRL_CCTRL(CONF_DAC1_CCTRL) + | (CONF_DAC1_RUNSTDBY << DAC_DACCTRL_RUNSTDBY_Pos) + | (CONF_DAC1_DITHER << DAC_DACCTRL_DITHER_Pos) + | (CONF_DAC1_REFRESH << DAC_DACCTRL_REFRESH_Pos), + (CONF_DAC_DBGRUN << DAC_DBGCTRL_DBGRUN_Pos)}; + +/*!< Pointer to hpl device */ +static struct _dac_async_device *_dac_dev = NULL; + +/** + * \brief Initialize synchronous DAC + */ +int32_t _dac_sync_init(struct _dac_sync_device *const device, void *const hw) +{ + ASSERT(device); + + device->hw = hw; + + return _dac_init(device->hw); +} + +/** +* \brief Initialize DAC +*/ +int32_t _dac_async_init(struct _dac_async_device *const device, void *const hw) +{ + int32_t init_status; + + ASSERT(device); + + init_status = _dac_init(hw); + if (init_status) { + return init_status; + } + device->hw = hw; + + _dac_dev = device; + NVIC_DisableIRQ(DAC_0_IRQn + 0); + NVIC_ClearPendingIRQ(DAC_0_IRQn + 0); + NVIC_EnableIRQ(DAC_0_IRQn + 0); + NVIC_DisableIRQ(DAC_0_IRQn + 1); + NVIC_ClearPendingIRQ(DAC_0_IRQn + 1); + NVIC_EnableIRQ(DAC_0_IRQn + 1); + NVIC_DisableIRQ(DAC_0_IRQn + 2); + NVIC_ClearPendingIRQ(DAC_0_IRQn + 2); + NVIC_EnableIRQ(DAC_0_IRQn + 2); + + return ERR_NONE; +} + +/** + * \brief De-initialize DAC + */ +void _dac_sync_deinit(struct _dac_sync_device *const device) +{ + _dac_deinit(device->hw); +} + +/** + * \brief De-initialize DAC + */ +void _dac_async_deinit(struct _dac_async_device *const device) +{ + NVIC_DisableIRQ(DAC_0_IRQn + 0); + NVIC_DisableIRQ(DAC_0_IRQn + 1); + NVIC_DisableIRQ(DAC_0_IRQn + 2); + + _dac_deinit(device->hw); +} + +/** + * \brief Enable DAC Channel + */ +void _dac_sync_enable_channel(struct _dac_sync_device *const device, const uint8_t ch) +{ + _dac_enable_channel(device->hw, ch); +} + +/** + * \brief Enable DAC Channel + */ +void _dac_async_enable_channel(struct _dac_async_device *const device, const uint8_t ch) +{ + _dac_enable_channel(device->hw, ch); +} + +/** + * \brief Disable DAC Channel + */ +void _dac_sync_disable_channel(struct _dac_sync_device *const device, const uint8_t ch) +{ + _dac_disable_channel(device->hw, ch); +} + +/** + * \brief Disable DAC Channel + */ +void _dac_async_disable_channel(struct _dac_async_device *const device, const uint8_t ch) +{ + _dac_disable_channel(device->hw, ch); +} + +/** + * \brief Checks if DAC channel is enabled + */ +bool _dac_sync_is_channel_enable(struct _dac_sync_device *const device, const uint8_t ch) +{ + return _dac_is_channel_enabled(device->hw, ch); +} + +/** + * \brief Checks if DAC channel is enabled + */ +bool _dac_async_is_channel_enable(struct _dac_async_device *const device, const uint8_t ch) +{ + return _dac_is_channel_enabled(device->hw, ch); +} + +/** + * \brief write synchronous DAC data for output + */ +void _dac_sync_write_data(struct _dac_sync_device *const device, const uint16_t data, const uint8_t ch) +{ + hri_dac_write_DATA_reg(device->hw, ch, data); +} + +/** + * \brief write DAC data for output + */ +void _dac_async_write_data(struct _dac_async_device *const device, const uint16_t data, const uint8_t ch) +{ + hri_dac_write_DATABUF_reg(device->hw, ch, data); +} + +/** + * \brief Enable/disable DAC interrupt + */ +void _dac_async_set_irq_state(struct _dac_async_device *const device, const enum _dac_callback_type type, + const bool state) +{ + void *hw = device->hw; + + if (DAC_DEVICE_CONVERSION_DONE_CB == type) { + hri_dac_write_INTEN_EMPTY0_bit(hw, state); + hri_dac_write_INTEN_EMPTY1_bit(hw, state); + } else if (DAC_DEVICE_ERROR_CB == type) { + hri_dac_write_INTEN_UNDERRUN0_bit(hw, state); + hri_dac_write_INTEN_UNDERRUN1_bit(hw, state); + } +} + +/** + * \internal Initialize DAC + * + * \param[in] hw The pointer to DAC hardware instance + * + * \return The status of initialization + */ +static int32_t _dac_init(void *const hw) +{ + ASSERT(hw == DAC); + + hri_dac_wait_for_sync(hw, DAC_SYNCBUSY_SWRST); + if (hri_dac_get_CTRLA_ENABLE_bit(hw)) { + return ERR_BUSY; + } + hri_dac_set_CTRLA_SWRST_bit(hw); + hri_dac_wait_for_sync(hw, DAC_SYNCBUSY_SWRST); + + hri_dac_write_CTRLB_reg(hw, _dac.ctrlb); + hri_dac_write_EVCTRL_reg(hw, _dac.ev_ctrl); + hri_dac_write_DACCTRL_reg(hw, 0, _dac.dac_ctrl0); + hri_dac_write_DACCTRL_reg(hw, 1, _dac.dac_ctrl1); + hri_dac_write_DBGCTRL_reg(hw, _dac.dbg_ctrl); + + return ERR_NONE; +} + +/** + * \internal De-initialize DAC + * + * \param[in] hw The pointer to DAC hardware instance + */ +static inline void _dac_deinit(void *const hw) +{ + hri_dac_clear_CTRLA_ENABLE_bit(hw); + hri_dac_set_CTRLA_SWRST_bit(hw); +} + +/** + * \internal Enable DAC channel + * + * \param[in] hw The pointer to hardware instance + * \param[in] ch The channel to enable + */ +static inline void _dac_enable_channel(void *const hw, const uint8_t ch) +{ + ASSERT(ch <= 1); + + hri_dac_clear_CTRLA_ENABLE_bit(hw); + hri_dac_set_DACCTRL_ENABLE_bit(hw, ch); + hri_dac_set_CTRLA_ENABLE_bit(hw); + + if (0 == ch) { + while (!hri_dac_get_STATUS_READY0_bit(hw)) + ; + } else { + while (!hri_dac_get_STATUS_READY1_bit(hw)) + ; + } +} + +/** + * \internal Disable DAC channel + * + * \param[in] hw The pointer to hardware instance + * \param[in] ch The channel to disable + */ +static inline void _dac_disable_channel(void *const hw, const uint8_t ch) +{ + ASSERT(ch <= 1); + + hri_dac_clear_CTRLA_ENABLE_bit(hw); + hri_dac_clear_DACCTRL_ENABLE_bit(hw, ch); + + if (hri_dac_get_DACCTRL_ENABLE_bit(hw, !ch)) { + hri_dac_set_CTRLA_ENABLE_bit(hw); + } +} + +/** + * \internal Checks if DAC channel is enabled + * + * \param[in] hw The pointer to hardware instance + * \param[in] ch The channel to check + */ +static inline bool _dac_is_channel_enabled(void *const hw, const uint8_t ch) +{ + if (ch <= 1) { + return hri_dac_get_DACCTRL_ENABLE_bit(hw, ch); + } + + return false; +} diff --git a/atmel-samd/asf4/samd51/hpl/dmac/hpl_dmac.c b/atmel-samd/asf4/samd51/hpl/dmac/hpl_dmac.c new file mode 100644 index 000000000..9a6602eb3 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/dmac/hpl_dmac.c @@ -0,0 +1,262 @@ + +/** + * \file + * + * \brief Generic DMAC related functionality. + * + * Copyright (C) 2016 - 2017 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ +#include <hpl_dma.h> +#include <utils_assert.h> +#include <utils.h> +#include <hpl_dmac_config.h> +#include <utils_repeat_macro.h> + +#if CONF_DMAC_ENABLE +/* Section containing first descriptors for all DMAC channels */ +COMPILER_ALIGNED(16) +static DmacDescriptor _descriptor_section[DMAC_CH_NUM]; + +/* Section containing current descriptors for all DMAC channels */ +COMPILER_ALIGNED(16) +static DmacDescriptor _write_back_section[DMAC_CH_NUM]; + +/* Array containing callbacks for DMAC channels */ +static struct _dma_resource _resources[DMAC_CH_NUM]; + +/* DMAC interrupt handler */ +static void _dmac_handler(void); + +/* This macro DMAC configuration */ +#define DMAC_CHANNEL_CFG(i, n) \ + {(CONF_DMAC_RUNSTDBY_##n << DMAC_CHCTRLA_RUNSTDBY_Pos) | DMAC_CHCTRLA_TRIGACT(CONF_DMAC_TRIGACT_##n) \ + | DMAC_CHCTRLA_TRIGSRC(CONF_DMAC_TRIGSRC_##n), \ + DMAC_CHPRILVL_PRILVL(CONF_DMAC_LVL_##n), \ + (CONF_DMAC_EVIE_##n << DMAC_CHEVCTRL_EVIE_Pos) | (CONF_DMAC_EVOE_##n << DMAC_CHEVCTRL_EVOE_Pos) \ + | (CONF_DMAC_EVACT_##n << DMAC_CHEVCTRL_EVACT_Pos), \ + DMAC_BTCTRL_STEPSIZE(CONF_DMAC_STEPSIZE_##n) | (CONF_DMAC_STEPSEL_##n << DMAC_BTCTRL_STEPSEL_Pos) \ + | (CONF_DMAC_DSTINC_##n << DMAC_BTCTRL_DSTINC_Pos) \ + | (CONF_DMAC_SRCINC_##n << DMAC_BTCTRL_SRCINC_Pos) \ + | DMAC_BTCTRL_BEATSIZE(CONF_DMAC_BEATSIZE_##n) \ + | DMAC_BTCTRL_BLOCKACT(CONF_DMAC_BLOCKACT_##n) \ + | DMAC_BTCTRL_EVOSEL(CONF_DMAC_EVOSEL_##n)}, + +/* DMAC channel configuration */ +struct dmac_channel_cfg { + uint32_t ctrla; + uint8_t prilvl; + uint8_t evctrl; + uint16_t btctrl; +}; + +/* DMAC channel configurations */ +const static struct dmac_channel_cfg _cfgs[] = {REPEAT_MACRO(DMAC_CHANNEL_CFG, i, DMAC_CH_NUM)}; + +/** + * \brief Initialize DMAC + */ +int32_t _dma_init(void) +{ + uint8_t i; + + hri_dmac_clear_CTRL_DMAENABLE_bit(DMAC); + hri_dmac_clear_CRCCTRL_reg(DMAC, DMAC_CRCCTRL_CRCSRC_Msk); + hri_dmac_set_CTRL_SWRST_bit(DMAC); + while (hri_dmac_get_CTRL_SWRST_bit(DMAC)) + ; + + hri_dmac_write_CTRL_reg(DMAC, + (CONF_DMAC_LVLEN0 << DMAC_CTRL_LVLEN0_Pos) | (CONF_DMAC_LVLEN1 << DMAC_CTRL_LVLEN1_Pos) + | (CONF_DMAC_LVLEN2 << DMAC_CTRL_LVLEN2_Pos) + | (CONF_DMAC_LVLEN3 << DMAC_CTRL_LVLEN3_Pos)); + hri_dmac_write_DBGCTRL_DBGRUN_bit(DMAC, CONF_DMAC_DBGRUN); + + hri_dmac_write_PRICTRL0_reg(DMAC, + DMAC_PRICTRL0_LVLPRI0(CONF_DMAC_LVLPRI0) | DMAC_PRICTRL0_LVLPRI1(CONF_DMAC_LVLPRI1) + | DMAC_PRICTRL0_LVLPRI2(CONF_DMAC_LVLPRI2) + | DMAC_PRICTRL0_LVLPRI3(CONF_DMAC_LVLPRI3) + | (CONF_DMAC_RRLVLEN0 << DMAC_PRICTRL0_RRLVLEN0_Pos) + | (CONF_DMAC_RRLVLEN1 << DMAC_PRICTRL0_RRLVLEN1_Pos) + | (CONF_DMAC_RRLVLEN2 << DMAC_PRICTRL0_RRLVLEN2_Pos) + | (CONF_DMAC_RRLVLEN3 << DMAC_PRICTRL0_RRLVLEN3_Pos)); + hri_dmac_write_BASEADDR_reg(DMAC, (uint32_t)_descriptor_section); + hri_dmac_write_WRBADDR_reg(DMAC, (uint32_t)_write_back_section); + + for (i = 0; i < DMAC_CH_NUM; i++) { + hri_dmac_write_CHCTRLA_reg(DMAC, i, _cfgs[i].ctrla); + hri_dmac_write_CHPRILVL_reg(DMAC, i, _cfgs[i].prilvl); + hri_dmac_write_CHEVCTRL_reg(DMAC, i, _cfgs[i].evctrl); + hri_dmacdescriptor_write_BTCTRL_reg(&_descriptor_section[i], _cfgs[i].btctrl); + } + + for (i = 0; i < 5; i++) { + NVIC_DisableIRQ(DMAC_0_IRQn + i); + NVIC_ClearPendingIRQ(DMAC_0_IRQn + i); + NVIC_EnableIRQ(DMAC_0_IRQn + i); + } + + hri_dmac_set_CTRL_DMAENABLE_bit(DMAC); + + return ERR_NONE; +} + +/** + * \brief Enable/disable DMA interrupt + */ +void _dma_set_irq_state(const uint8_t channel, const enum _dma_callback_type type, const bool state) +{ + if (DMA_TRANSFER_COMPLETE_CB == type) { + hri_dmac_write_CHINTEN_TCMPL_bit(DMAC, channel, state); + } else if (DMA_TRANSFER_ERROR_CB == type) { + hri_dmac_write_CHINTEN_TERR_bit(DMAC, channel, state); + } +} + +int32_t _dma_set_destination_address(const uint8_t channel, const void *const dst) +{ + hri_dmacdescriptor_write_DSTADDR_reg(&_descriptor_section[channel], (uint32_t)dst); + + return ERR_NONE; +} + +int32_t _dma_set_source_address(const uint8_t channel, const void *const src) +{ + hri_dmacdescriptor_write_SRCADDR_reg(&_descriptor_section[channel], (uint32_t)src); + + return ERR_NONE; +} + +int32_t _dma_srcinc_enable(const uint8_t channel, const bool enable) +{ + hri_dmacdescriptor_write_BTCTRL_SRCINC_bit(&_descriptor_section[channel], enable); + + return ERR_NONE; +} + +int32_t _dma_set_data_amount(const uint8_t channel, const uint32_t amount) +{ + uint32_t address = hri_dmacdescriptor_read_DSTADDR_reg(&_descriptor_section[channel]); + uint8_t beat_size = hri_dmacdescriptor_read_BTCTRL_BEATSIZE_bf(&_descriptor_section[channel]); + + if (hri_dmacdescriptor_get_BTCTRL_DSTINC_bit(&_descriptor_section[channel])) { + hri_dmacdescriptor_write_DSTADDR_reg(&_descriptor_section[channel], address + amount * (1 << beat_size)); + } + + address = hri_dmacdescriptor_read_SRCADDR_reg(&_descriptor_section[channel]); + + if (hri_dmacdescriptor_get_BTCTRL_SRCINC_bit(&_descriptor_section[channel])) { + hri_dmacdescriptor_write_SRCADDR_reg(&_descriptor_section[channel], address + amount * (1 << beat_size)); + } + + hri_dmacdescriptor_write_BTCNT_reg(&_descriptor_section[channel], amount); + + return ERR_NONE; +} + +int32_t _dma_enable_transaction(const uint8_t channel, const bool software_trigger) +{ + hri_dmacdescriptor_set_BTCTRL_VALID_bit(&_descriptor_section[channel]); + hri_dmac_set_CHCTRLA_ENABLE_bit(DMAC, channel); + + if (software_trigger) { + hri_dmac_set_SWTRIGCTRL_reg(DMAC, 1 << channel); + } + + return ERR_NONE; +} + +int32_t _dma_get_channel_resource(struct _dma_resource **resource, const uint8_t channel) +{ + *resource = &_resources[channel]; + + return ERR_NONE; +} + +/** + * \internal DMAC interrupt handler + */ +static void _dmac_handler(void) +{ + uint8_t channel = hri_dmac_get_INTPEND_reg(DMAC, DMAC_INTPEND_ID_Msk); + struct _dma_resource *tmp_resource = &_resources[channel]; + + if (hri_dmac_get_CHINTFLAG_TERR_bit(DMAC, channel)) { + hri_dmac_clear_CHINTFLAG_TERR_bit(DMAC, channel); + tmp_resource->dma_cb.error(tmp_resource); + } else if (hri_dmac_get_CHINTFLAG_TCMPL_bit(DMAC, channel)) { + hri_dmac_clear_CHINTFLAG_TCMPL_bit(DMAC, channel); + tmp_resource->dma_cb.transfer_done(tmp_resource); + } +} +/** +* \brief DMAC interrupt handler +*/ +void DMAC_0_Handler(void) +{ + _dmac_handler(); +} +/** +* \brief DMAC interrupt handler +*/ +void DMAC_1_Handler(void) +{ + _dmac_handler(); +} +/** +* \brief DMAC interrupt handler +*/ +void DMAC_2_Handler(void) +{ + _dmac_handler(); +} +/** +* \brief DMAC interrupt handler +*/ +void DMAC_3_Handler(void) +{ + _dmac_handler(); +} +/** +* \brief DMAC interrupt handler +*/ +void DMAC_4_Handler(void) +{ + _dmac_handler(); +} + +#endif /* CONF_DMAC_ENABLE */ diff --git a/atmel-samd/asf4/samd51/hpl/evsys/hpl_evsys.c b/atmel-samd/asf4/samd51/hpl/evsys/hpl_evsys.c new file mode 100644 index 000000000..024d24527 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/evsys/hpl_evsys.c @@ -0,0 +1,151 @@ + +/** + * \file + * + * \brief EVSYS related functionality implementation. + * + * Copyright (C) 2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_evsys_config.h> +#include <hpl_init.h> +#include <utils_repeat_macro.h> +#include <err_codes.h> + +/* Stub macros for reserved channel */ +#define CONF_CHANNEL_38 0 +#define CONF_ONDEMAND_38 0 +#define CONF_RUNSTDBY_38 0 +#define CONF_EDGSEL_38 0 +#define CONF_PATH_38 0 +#define CONF_EVGEN_38 0 + +#define CONF_CHANNEL_39 0 +#define CONF_ONDEMAND_39 0 +#define CONF_RUNSTDBY_39 0 +#define CONF_EDGSEL_39 0 +#define CONF_PATH_39 0 +#define CONF_EVGEN_39 0 + +#define CONF_CHANNEL_113 0 +#define CONF_ONDEMAND_113 0 +#define CONF_RUNSTDBY_113 0 +#define CONF_EDGSEL_113 0 +#define CONF_PATH_113 0 +#define CONF_EVGEN_113 0 + +/* Event user configuration */ +#define USER_MUX_CONF(i, n) EVSYS_USER_CHANNEL(CONF_CHANNEL_##n), + +/* This macro is used for repeat macro: i - unused, n - amount of channels. + * It contains channel configuration. */ +#define CHANNEL_CONF(i, n) \ + (CONF_ONDEMAND_##n << EVSYS_CHANNEL_ONDEMAND_Pos) | (CONF_RUNSTDBY_##n << EVSYS_CHANNEL_RUNSTDBY_Pos) \ + | EVSYS_CHANNEL_EDGSEL(CONF_EDGSEL_##n) | EVSYS_CHANNEL_PATH(CONF_PATH_##n) \ + | EVSYS_CHANNEL_EVGEN(CONF_EVGEN_##n), + +/* This macro is used for repeat macro: i - unused, n - amount of channels + * It contains interrupts configuration. */ +#define INT_CFG(i, n) (CONF_OVR_##n << EVSYS_CHINTFLAG_OVR_Pos) | (CONF_EVD_##n << EVSYS_CHINTFLAG_EVD_Pos), + +static const uint8_t user_mux_confs[] = {REPEAT_MACRO(USER_MUX_CONF, i, EVSYS_USERS)}; + +static const uint16_t channel_confs[] = {REPEAT_MACRO(CHANNEL_CONF, i, EVSYS_CHANNELS)}; + +static const uint32_t interrupt_cfg[] = {REPEAT_MACRO(INT_CFG, i, EVSYS_CHANNELS)}; + +/** + * \brief Initialize event system + */ +int32_t _event_system_init(void) +{ + uint8_t i; + + /* configure user multiplexers */ + for (i = 0; i < EVSYS_USERS; i++) { + hri_evsys_write_USER_reg(EVSYS, i, user_mux_confs[i]); + } + + /* configure channels */ + for (i = 0; i < EVSYS_CHANNELS; i++) { + hri_evsys_write_CHANNEL_reg(EVSYS, i, channel_confs[i]); + hri_evsys_write_CHINTEN_reg(EVSYS, i, interrupt_cfg[i]); + } + + return ERR_NONE; +} + +/** + * \brief De-initialize event system. + */ +int32_t _event_system_deinit() +{ + hri_evsys_write_CTRLA_reg(EVSYS, EVSYS_CTRLA_SWRST); + + return ERR_NONE; +} + +/** + * \brief Enable/disable event reception by the given user from the given + * channel + */ +int32_t _event_system_enable_user(const uint16_t user, const uint16_t channel, const bool on) +{ + if (on) { + hri_evsys_write_USER_reg(EVSYS, user, channel); + } else { + hri_evsys_write_USER_reg(EVSYS, user, 0); + } + + return ERR_NONE; +} + +/** + * \brief Enable/disable event generation by the given generator for the given + * channel + */ +int32_t _event_system_enable_generator(const uint16_t generator, const uint16_t channel, const bool on) +{ + if (on) { + hri_evsys_write_CHANNEL_EVGEN_bf(EVSYS, channel, generator); + } else { + hri_evsys_write_CHANNEL_EVGEN_bf(EVSYS, channel, 0); + } + + return ERR_NONE; +} diff --git a/atmel-samd/asf4/samd51/hpl/gclk/hpl_gclk.c b/atmel-samd/asf4/samd51/hpl/gclk/hpl_gclk.c new file mode 100644 index 000000000..d022c24f9 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/gclk/hpl_gclk.c @@ -0,0 +1,200 @@ + +/** + * \file + * + * \brief Generic Clock Controller related functionality. + * + * Copyright (C) 2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_gclk_config.h> +#include <hpl_init.h> +#include <utils_assert.h> + +/** + * \brief Initializes generators + + */ +void _gclk_init_generators(void) +{ + +#if CONF_GCLK_GEN_0_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 0, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_0_DIV) + | (CONF_GCLK_GEN_0_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_0_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_0_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_0_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_0_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_0_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_0_SOURCE); +#endif +#if CONF_GCLK_GEN_1_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 1, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_1_DIV) + | (CONF_GCLK_GEN_1_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_1_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_1_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_1_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_1_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_1_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_1_SOURCE); +#endif +#if CONF_GCLK_GEN_2_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 2, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_2_DIV) + | (CONF_GCLK_GEN_2_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_2_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_2_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_2_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_2_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_2_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_2_SOURCE); +#endif +#if CONF_GCLK_GEN_3_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 3, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_3_DIV) + | (CONF_GCLK_GEN_3_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_3_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_3_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_3_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_3_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_3_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_3_SOURCE); +#endif +#if CONF_GCLK_GEN_4_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 4, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_4_DIV) + | (CONF_GCLK_GEN_4_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_4_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_4_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_4_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_4_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_4_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_4_SOURCE); +#endif +#if CONF_GCLK_GEN_5_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 5, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_5_DIV) + | (CONF_GCLK_GEN_5_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_5_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_5_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_5_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_5_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_5_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_5_SOURCE); +#endif +#if CONF_GCLK_GEN_6_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 6, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_6_DIV) + | (CONF_GCLK_GEN_6_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_6_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_6_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_6_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_6_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_6_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_6_SOURCE); +#endif +#if CONF_GCLK_GEN_7_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 7, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_7_DIV) + | (CONF_GCLK_GEN_7_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_7_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_7_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_7_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_7_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_7_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_7_SOURCE); +#endif +#if CONF_GCLK_GEN_8_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 8, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_8_DIV) + | (CONF_GCLK_GEN_8_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_8_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_8_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_8_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_8_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_8_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_8_SOURCE); +#endif +#if CONF_GCLK_GEN_9_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 9, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_9_DIV) + | (CONF_GCLK_GEN_9_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_9_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_9_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_9_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_9_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_9_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_9_SOURCE); +#endif +#if CONF_GCLK_GEN_10_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 10, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_10_DIV) + | (CONF_GCLK_GEN_10_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_10_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_10_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_10_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_10_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_10_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_10_SOURCE); +#endif +#if CONF_GCLK_GEN_11_GENEN == 1 + hri_gclk_write_GENCTRL_reg(GCLK, + 11, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_11_DIV) + | (CONF_GCLK_GEN_11_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_11_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) + | (CONF_GCLK_GEN_11_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_11_OOV << GCLK_GENCTRL_OOV_Pos) + | (CONF_GCLK_GEN_11_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GEN_11_GENEN << GCLK_GENCTRL_GENEN_Pos) + | CONF_GCLK_GEN_11_SOURCE); +#endif +} diff --git a/atmel-samd/asf4/samd51/hpl/gclk/hpl_gclk_base.h b/atmel-samd/asf4/samd51/hpl/gclk/hpl_gclk_base.h new file mode 100644 index 000000000..701a03887 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/gclk/hpl_gclk_base.h @@ -0,0 +1,90 @@ +/** + * \file + * + * \brief Generic Clock Controller. + * + * Copyright (C) 2014 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#ifndef _HPL_GCLK_H_INCLUDED +#define _HPL_GCLK_H_INCLUDED + +#include <compiler.h> +#ifdef _UNIT_TEST_ +#include <hri_gclk1_v210_mock.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup gclk_group GCLK Hardware Proxy Layer + * + * \section gclk_hpl_rev Revision History + * - v0.0.0.1 Initial Commit + * + *@{ + */ + +/** + * \name HPL functions + */ +//@{ +/** + * \brief Enable clock on the given channel with the given clock source + * + * This function maps the given clock source to the given clock channel + * and enables channel. + * + * \param[in] channel The channel to enable clock for + * \param[in] source The clock source for the given channel + */ +static inline void _gclk_enable_channel(const uint8_t channel, const uint8_t source) +{ + + hri_gclk_write_PCHCTRL_reg(GCLK, channel, source | GCLK_PCHCTRL_CHEN); +} + +//@} +/**@}*/ +#ifdef __cplusplus +} +#endif + +#endif /* _HPL_GCLK_H_INCLUDED */ diff --git a/atmel-samd/asf4/samd51/hpl/mclk/hpl_mclk.c b/atmel-samd/asf4/samd51/hpl/mclk/hpl_mclk.c new file mode 100644 index 000000000..0788a40c0 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/mclk/hpl_mclk.c @@ -0,0 +1,54 @@ +/** + * \file + * + * \brief SAM Main Clock. + * + * Copyright (C) 2015-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <compiler.h> +#include <hpl_mclk_config.h> + +/** + * \brief Initialize master clock generator + */ +void _mclk_init(void) +{ + void *hw = (void *)MCLK; + hri_mclk_write_CPUDIV_reg(hw, MCLK_CPUDIV_DIV(CONF_MCLK_CPUDIV)); +} diff --git a/atmel-samd/asf4/samd51/hpl/osc32kctrl/hpl_osc32kctrl.c b/atmel-samd/asf4/samd51/hpl/osc32kctrl/hpl_osc32kctrl.c new file mode 100644 index 000000000..99ed873b4 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/osc32kctrl/hpl_osc32kctrl.c @@ -0,0 +1,94 @@ + +/** + * \file + * + * \brief SAM 32k Oscillators Controller. + * + * Copyright (C) 2015-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ +#include <hpl_init.h> +#include <hpl_osc32kctrl_config.h> + +/** + * \brief Initialize 32 kHz clock sources + */ +void _osc32kctrl_init_sources(void) +{ + void * hw = (void *)OSC32KCTRL; + uint16_t calib = 0; + +#if CONF_XOSC32K_CONFIG == 1 + hri_osc32kctrl_write_XOSC32K_reg(hw, + OSC32KCTRL_XOSC32K_STARTUP(CONF_XOSC32K_STARTUP) + | (CONF_XOSC32K_ONDEMAND << OSC32KCTRL_XOSC32K_ONDEMAND_Pos) + | (CONF_XOSC32K_RUNSTDBY << OSC32KCTRL_XOSC32K_RUNSTDBY_Pos) + | (CONF_XOSC32K_EN1K << OSC32KCTRL_XOSC32K_EN1K_Pos) + | (CONF_XOSC32K_EN32K << OSC32KCTRL_XOSC32K_EN32K_Pos) + | (CONF_XOSC32K_XTALEN << OSC32KCTRL_XOSC32K_XTALEN_Pos) + | +#ifdef CONF_XOSC32K_CGM + OSC32KCTRL_XOSC32K_CGM(CONF_XOSC32K_CGM) | +#endif + (CONF_XOSC32K_ENABLE << OSC32KCTRL_XOSC32K_ENABLE_Pos)); + + hri_osc32kctrl_write_CFDCTRL_reg(hw, (CONF_XOSC32K_CFDEN << OSC32KCTRL_CFDCTRL_CFDEN_Pos)); + + hri_osc32kctrl_write_EVCTRL_reg(hw, (CONF_XOSC32K_CFDEO << OSC32KCTRL_EVCTRL_CFDEO_Pos)); +#endif + +#if CONF_OSCULP32K_CONFIG == 1 + calib = hri_osc32kctrl_read_OSCULP32K_CALIB_bf(hw); + hri_osc32kctrl_write_OSCULP32K_reg(hw, +#if CONF_OSCULP32K_CALIB_ENABLE == 1 + OSC32KCTRL_OSCULP32K_CALIB(CONF_OSCULP32K_CALIB) +#else + OSC32KCTRL_OSCULP32K_CALIB(calib) +#endif + ); +#endif + +#if CONF_XOSC32K_CONFIG +#if CONF_XOSC32K_ENABLE == 1 && CONF_XOSC32K_ONDEMAND == 0 + while (!hri_osc32kctrl_get_STATUS_XOSC32KRDY_bit(hw)) + ; +#endif +#endif + + hri_osc32kctrl_write_RTCCTRL_reg(hw, OSC32KCTRL_RTCCTRL_RTCSEL(CONF_RTCCTRL)); + (void)calib; +} diff --git a/atmel-samd/asf4/samd51/hpl/oscctrl/hpl_oscctrl.c b/atmel-samd/asf4/samd51/hpl/oscctrl/hpl_oscctrl.c new file mode 100644 index 000000000..83f00dceb --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/oscctrl/hpl_oscctrl.c @@ -0,0 +1,244 @@ + +/** + * \file + * + * \brief SAM Oscillators Controller. + * + * Copyright (C) 2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_init.h> +#include <hpl_oscctrl_config.h> +#include <hpl_gclk_config.h> + +/** + * \brief Initialize clock sources + */ +void _oscctrl_init_sources(void) +{ + void *hw = (void *)OSCCTRL; + +#if CONF_XOSC0_CONFIG == 1 + hri_oscctrl_write_XOSCCTRL_reg(hw, + 0, + OSCCTRL_XOSCCTRL_CFDPRESC(CONF_XOSC0_CFDPRESC) + | OSCCTRL_XOSCCTRL_STARTUP(CONF_XOSC0_STARTUP) + | (CONF_XOSC0_SWBEN << OSCCTRL_XOSCCTRL_SWBEN_Pos) + | (CONF_XOSC0_CFDEN << OSCCTRL_XOSCCTRL_CFDEN_Pos) + | (CONF_XOSC0_ENALC << OSCCTRL_XOSCCTRL_ENALC_Pos) + | OSCCTRL_XOSCCTRL_IMULT(CONF_XOSC0_IMULT) + | OSCCTRL_XOSCCTRL_IPTAT(CONF_XOSC0_IPTAT) + | (CONF_XOSC0_LOWBUFGAIN << OSCCTRL_XOSCCTRL_LOWBUFGAIN_Pos) + | (CONF_XOSC0_ONDEMAND << OSCCTRL_XOSCCTRL_ONDEMAND_Pos) + | (CONF_XOSC0_RUNSTDBY << OSCCTRL_XOSCCTRL_RUNSTDBY_Pos) + | (CONF_XOSC0_XTALEN << OSCCTRL_XOSCCTRL_XTALEN_Pos) + | (CONF_XOSC0_ENABLE << OSCCTRL_XOSCCTRL_ENABLE_Pos)); +#endif + +#if CONF_XOSC0_CONFIG == 1 +#if CONF_XOSC0_ENABLE == 1 + while (!hri_oscctrl_get_STATUS_XOSCRDY0_bit(hw)) + ; +#endif +#if CONF_XOSC0_ONDEMAND == 1 + hri_oscctrl_set_XOSCCTRL_ONDEMAND_bit(hw, 0); +#endif +#endif + +#if CONF_XOSC1_CONFIG == 1 + hri_oscctrl_write_XOSCCTRL_reg(hw, + 1, + OSCCTRL_XOSCCTRL_CFDPRESC(CONF_XOSC1_CFDPRESC) + | OSCCTRL_XOSCCTRL_STARTUP(CONF_XOSC1_STARTUP) + | (CONF_XOSC1_SWBEN << OSCCTRL_XOSCCTRL_SWBEN_Pos) + | (CONF_XOSC1_CFDEN << OSCCTRL_XOSCCTRL_CFDEN_Pos) + | (CONF_XOSC1_ENALC << OSCCTRL_XOSCCTRL_ENALC_Pos) + | OSCCTRL_XOSCCTRL_IMULT(CONF_XOSC1_IMULT) + | OSCCTRL_XOSCCTRL_IPTAT(CONF_XOSC1_IPTAT) + | (CONF_XOSC1_LOWBUFGAIN << OSCCTRL_XOSCCTRL_LOWBUFGAIN_Pos) + | (CONF_XOSC1_ONDEMAND << OSCCTRL_XOSCCTRL_ONDEMAND_Pos) + | (CONF_XOSC1_RUNSTDBY << OSCCTRL_XOSCCTRL_RUNSTDBY_Pos) + | (CONF_XOSC1_XTALEN << OSCCTRL_XOSCCTRL_XTALEN_Pos) + | (CONF_XOSC1_ENABLE << OSCCTRL_XOSCCTRL_ENABLE_Pos)); +#endif + +#if CONF_XOSC1_CONFIG == 1 +#if CONF_XOSC1_ENABLE == 1 + while (!hri_oscctrl_get_STATUS_XOSCRDY1_bit(hw)) + ; +#endif +#if CONF_XOSC1_ONDEMAND == 1 + hri_oscctrl_set_XOSCCTRL_ONDEMAND_bit(hw, 1); +#endif +#endif + + (void)hw; +} + +void _oscctrl_init_referenced_generators(void) +{ + void *hw = (void *)OSCCTRL; + +#if CONF_DFLL_CONFIG == 1 + hri_gclk_write_GENCTRL_SRC_bf(GCLK, 0, GCLK_GENCTRL_SRC_OSCULP32K); + uint8_t tmp; + hri_oscctrl_write_DFLLCTRLA_reg(hw, 0); +#if CONF_DFLL_USBCRM != 1 && CONF_DFLL_MODE != 0 + hri_gclk_write_PCHCTRL_reg( + GCLK, OSCCTRL_GCLK_ID_DFLL48, (1 << GCLK_PCHCTRL_CHEN_Pos) | GCLK_PCHCTRL_GEN(CONF_DFLL_GCLK)); +#endif + + hri_oscctrl_write_DFLLMUL_reg(hw, + OSCCTRL_DFLLMUL_CSTEP(CONF_DFLL_CSTEP) | OSCCTRL_DFLLMUL_FSTEP(CONF_DFLL_FSTEP) + | OSCCTRL_DFLLMUL_MUL(CONF_DFLL_MUL)); + while (hri_oscctrl_get_DFLLSYNC_DFLLMUL_bit(hw)) + ; + + hri_oscctrl_write_DFLLCTRLB_reg(hw, 0); + while (hri_oscctrl_get_DFLLSYNC_DFLLCTRLB_bit(hw)) + ; + + tmp = (CONF_DFLL_RUNSTDBY << OSCCTRL_DFLLCTRLA_RUNSTDBY_Pos) | OSCCTRL_DFLLCTRLA_ENABLE; + hri_oscctrl_write_DFLLCTRLA_reg(hw, tmp); + while (hri_oscctrl_get_DFLLSYNC_ENABLE_bit(hw)) + ; + +#if CONF_DFLL_OVERWRITE_CALIBRATION == 1 + hri_oscctrl_write_DFLLVAL_reg(hw, OSCCTRL_DFLLVAL_COARSE(CONF_DFLL_COARSE) | OSCCTRL_DFLLVAL_FINE(CONF_DFLL_FINE)); +#endif + hri_oscctrl_write_DFLLVAL_reg(hw, hri_oscctrl_read_DFLLVAL_reg(hw)); + while (hri_oscctrl_get_DFLLSYNC_DFLLVAL_bit(hw)) + ; + + tmp = (CONF_DFLL_WAITLOCK << OSCCTRL_DFLLCTRLB_WAITLOCK_Pos) | (CONF_DFLL_BPLCKC << OSCCTRL_DFLLCTRLB_BPLCKC_Pos) + | (CONF_DFLL_QLDIS << OSCCTRL_DFLLCTRLB_QLDIS_Pos) | (CONF_DFLL_CCDIS << OSCCTRL_DFLLCTRLB_CCDIS_Pos) + | (CONF_DFLL_USBCRM << OSCCTRL_DFLLCTRLB_USBCRM_Pos) | (CONF_DFLL_LLAW << OSCCTRL_DFLLCTRLB_LLAW_Pos) + | (CONF_DFLL_STABLE << OSCCTRL_DFLLCTRLB_STABLE_Pos) | (CONF_DFLL_MODE << OSCCTRL_DFLLCTRLB_MODE_Pos) | 0; + hri_oscctrl_write_DFLLCTRLB_reg(hw, tmp); + while (hri_oscctrl_get_DFLLSYNC_DFLLCTRLB_bit(hw)) + ; +#endif + +#if CONF_FDPLL0_CONFIG == 1 +#if CONF_FDPLL0_REFCLK == 0 + hri_gclk_write_PCHCTRL_reg( + GCLK, OSCCTRL_GCLK_ID_FDPLL0, (1 << GCLK_PCHCTRL_CHEN_Pos) | GCLK_PCHCTRL_GEN(CONF_FDPLL0_GCLK)); +#endif + hri_oscctrl_write_DPLLRATIO_reg( + hw, 0, OSCCTRL_DPLLRATIO_LDRFRAC(CONF_FDPLL0_LDRFRAC) | OSCCTRL_DPLLRATIO_LDR(CONF_FDPLL0_LDR)); + hri_oscctrl_write_DPLLCTRLB_reg(hw, + 0, + OSCCTRL_DPLLCTRLB_DIV(CONF_FDPLL0_DIV) + | (CONF_FDPLL0_DCOEN << OSCCTRL_DPLLCTRLB_DCOEN_Pos) + | OSCCTRL_DPLLCTRLB_DCOFILTER(CONF_FDPLL0_DCOFILTER) + | (CONF_FDPLL0_LBYPASS << OSCCTRL_DPLLCTRLB_LBYPASS_Pos) + | OSCCTRL_DPLLCTRLB_LTIME(CONF_FDPLL0_LTIME) + | OSCCTRL_DPLLCTRLB_REFCLK(CONF_FDPLL0_REFCLK) + | (CONF_FDPLL0_WUF << OSCCTRL_DPLLCTRLB_WUF_Pos) + | OSCCTRL_DPLLCTRLB_FILTER(CONF_FDPLL0_FILTER)); + hri_oscctrl_write_DPLLCTRLA_reg(hw, + 0, + (CONF_FDPLL0_RUNSTDBY << OSCCTRL_DPLLCTRLA_RUNSTDBY_Pos) + | (CONF_FDPLL0_ENABLE << OSCCTRL_DPLLCTRLA_ENABLE_Pos)); +#endif + +#if CONF_FDPLL1_CONFIG == 1 +#if CONF_FDPLL1_REFCLK == 0 + hri_gclk_write_PCHCTRL_reg( + GCLK, OSCCTRL_GCLK_ID_FDPLL1, (1 << GCLK_PCHCTRL_CHEN_Pos) | GCLK_PCHCTRL_GEN(CONF_FDPLL1_GCLK)); +#endif + hri_oscctrl_write_DPLLRATIO_reg( + hw, 1, OSCCTRL_DPLLRATIO_LDRFRAC(CONF_FDPLL1_LDRFRAC) | OSCCTRL_DPLLRATIO_LDR(CONF_FDPLL1_LDR)); + hri_oscctrl_write_DPLLCTRLB_reg(hw, + 1, + OSCCTRL_DPLLCTRLB_DIV(CONF_FDPLL1_DIV) + | (CONF_FDPLL1_DCOEN << OSCCTRL_DPLLCTRLB_DCOEN_Pos) + | OSCCTRL_DPLLCTRLB_DCOFILTER(CONF_FDPLL1_DCOFILTER) + | (CONF_FDPLL1_LBYPASS << OSCCTRL_DPLLCTRLB_LBYPASS_Pos) + | OSCCTRL_DPLLCTRLB_LTIME(CONF_FDPLL1_LTIME) + | OSCCTRL_DPLLCTRLB_REFCLK(CONF_FDPLL1_REFCLK) + | (CONF_FDPLL1_WUF << OSCCTRL_DPLLCTRLB_WUF_Pos) + | OSCCTRL_DPLLCTRLB_FILTER(CONF_FDPLL1_FILTER)); + hri_oscctrl_write_DPLLCTRLA_reg(hw, + 1, + (CONF_FDPLL1_RUNSTDBY << OSCCTRL_DPLLCTRLA_RUNSTDBY_Pos) + | (CONF_FDPLL1_ENABLE << OSCCTRL_DPLLCTRLA_ENABLE_Pos)); +#endif + +#if CONF_DFLL_CONFIG == 1 + if (hri_oscctrl_get_DFLLCTRLB_MODE_bit(hw)) { + hri_oscctrl_status_reg_t status_mask = OSCCTRL_STATUS_DFLLRDY | OSCCTRL_STATUS_DFLLLCKC; + + while (hri_oscctrl_get_STATUS_reg(hw, status_mask) != status_mask) + ; + } else { + while (!hri_oscctrl_get_STATUS_DFLLRDY_bit(hw)) + ; + } +#if CONF_DFLL_ONDEMAND == 1 + hri_oscctrl_set_DFLLCTRLA_ONDEMAND_bit(hw); +#endif +#endif + +#if CONF_FDPLL0_CONFIG == 1 +#if CONF_FDPLL0_ENABLE == 1 + while (!(hri_oscctrl_get_DPLLSTATUS_LOCK_bit(hw, 0) || hri_oscctrl_get_DPLLSTATUS_CLKRDY_bit(hw, 0))) + ; +#endif +#if CONF_FDPLL0_ONDEMAND == 1 + hri_oscctrl_set_DPLLCTRLA_ONDEMAND_bit(hw, 0); +#endif +#endif + +#if CONF_FDPLL1_CONFIG == 1 +#if CONF_FDPLL1_ENABLE == 1 + while (!(hri_oscctrl_get_DPLLSTATUS_LOCK_bit(hw, 1) || hri_oscctrl_get_DPLLSTATUS_CLKRDY_bit(hw, 1))) + ; +#endif +#if CONF_FDPLL1_ONDEMAND == 1 + hri_oscctrl_set_DPLLCTRLA_ONDEMAND_bit(hw, 1); +#endif +#endif + +#if CONF_DFLL_CONFIG == 1 + while (hri_gclk_read_SYNCBUSY_reg(GCLK)) + ; + hri_gclk_write_GENCTRL_SRC_bf(GCLK, 0, CONF_GCLK_GEN_0_SOURCE); +#endif + (void)hw; +} diff --git a/atmel-samd/asf4/samd51/hpl/pm/hpl_pm.c b/atmel-samd/asf4/samd51/hpl/pm/hpl_pm.c new file mode 100644 index 000000000..a46500e4a --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/pm/hpl_pm.c @@ -0,0 +1,65 @@ + +/** + * \file + * + * \brief SAM Power manager + * + * Copyright (C) 2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_sleep.h> +#include <hpl_init.h> + +/** + * \brief Set the sleep mode for the device + */ +int32_t _set_sleep_mode(const uint8_t mode) +{ + switch (mode) { + case 2: + case 4: + case 5: + case 6: + hri_pm_write_SLEEPCFG_SLEEPMODE_bf(PM, mode); + break; + default: + return ERR_INVALID_ARG; + } + + return ERR_NONE; +} diff --git a/atmel-samd/asf4/samd51/hpl/pm/hpl_pm_base.h b/atmel-samd/asf4/samd51/hpl/pm/hpl_pm_base.h new file mode 100644 index 000000000..34b044d7c --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/pm/hpl_pm_base.h @@ -0,0 +1,55 @@ +/** + * \file + * + * \brief SAM Power manager + * + * Copyright (C) 2014-2017 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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 distributionn. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + */ + +#ifndef _HPL_PM_BASE_H_INCLUDED +#define _HPL_PM_BASE_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include <utils_assert.h> + +#ifdef __cplusplus +} +#endif +#endif /* _HPL_PM_BASE_H_INCLUDED */ diff --git a/atmel-samd/asf4/samd51/hpl/port/hpl_gpio_base.h b/atmel-samd/asf4/samd51/hpl/port/hpl_gpio_base.h new file mode 100644 index 000000000..5ea315af3 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/port/hpl_gpio_base.h @@ -0,0 +1,172 @@ +/** + * \file + * + * \brief SAM PORT. + * + * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ +#include <compiler.h> +#include <hpl_gpio.h> +#include <utils_assert.h> + +/** + * \brief Set direction on port with mask + */ +static inline void _gpio_set_direction(const enum gpio_port port, const uint32_t mask, + const enum gpio_direction direction) +{ + switch (direction) { + case GPIO_DIRECTION_OFF: + hri_port_clear_DIR_reg(PORT, port, mask); + hri_port_write_WRCONFIG_reg(PORT, port, PORT_WRCONFIG_WRPINCFG | (mask & 0xffff)); + hri_port_write_WRCONFIG_reg( + PORT, port, PORT_WRCONFIG_HWSEL | PORT_WRCONFIG_WRPINCFG | ((mask & 0xffff0000) >> 16)); + break; + + case GPIO_DIRECTION_IN: + hri_port_clear_DIR_reg(PORT, port, mask); + hri_port_write_WRCONFIG_reg(PORT, port, PORT_WRCONFIG_WRPINCFG | PORT_WRCONFIG_INEN | (mask & 0xffff)); + hri_port_write_WRCONFIG_reg(PORT, + port, + PORT_WRCONFIG_HWSEL | PORT_WRCONFIG_WRPINCFG | PORT_WRCONFIG_INEN + | ((mask & 0xffff0000) >> 16)); + break; + + case GPIO_DIRECTION_OUT: + hri_port_set_DIR_reg(PORT, port, mask); + hri_port_write_WRCONFIG_reg(PORT, port, PORT_WRCONFIG_WRPINCFG | (mask & 0xffff)); + hri_port_write_WRCONFIG_reg( + PORT, port, PORT_WRCONFIG_HWSEL | PORT_WRCONFIG_WRPINCFG | ((mask & 0xffff0000) >> 16)); + break; + + default: + ASSERT(false); + } +} + +/** + * \brief Set output level on port with mask + */ +static inline void _gpio_set_level(const enum gpio_port port, const uint32_t mask, const bool level) +{ + if (level) { + hri_port_set_OUT_reg(PORT, port, mask); + } else { + hri_port_clear_OUT_reg(PORT, port, mask); + } +} + +/** + * \brief Change output level to the opposite with mask + */ +static inline void _gpio_toggle_level(const enum gpio_port port, const uint32_t mask) +{ + hri_port_toggle_OUT_reg(PORT, port, mask); +} + +/** + * \brief Get input levels on all port pins + */ +static inline uint32_t _gpio_get_level(const enum gpio_port port) +{ + uint32_t tmp; + + CRITICAL_SECTION_ENTER(); + + uint32_t dir_tmp = hri_port_read_DIR_reg(PORT, port); + + tmp = hri_port_read_IN_reg(PORT, port) & ~dir_tmp; + tmp |= hri_port_read_OUT_reg(PORT, port) & dir_tmp; + + CRITICAL_SECTION_LEAVE(); + + return tmp; +} + +/** + * \brief Set pin pull mode + */ +static inline void _gpio_set_pin_pull_mode(const enum gpio_port port, const uint8_t pin, + const enum gpio_pull_mode pull_mode) +{ + switch (pull_mode) { + case GPIO_PULL_OFF: + hri_port_clear_PINCFG_PULLEN_bit(PORT, port, pin); + break; + + case GPIO_PULL_UP: + hri_port_clear_DIR_reg(PORT, port, 1U << pin); + hri_port_set_PINCFG_PULLEN_bit(PORT, port, pin); + hri_port_set_OUT_reg(PORT, port, 1U << pin); + break; + + case GPIO_PULL_DOWN: + hri_port_clear_DIR_reg(PORT, port, 1U << pin); + hri_port_set_PINCFG_PULLEN_bit(PORT, port, pin); + hri_port_clear_OUT_reg(PORT, port, 1U << pin); + break; + + default: + ASSERT(false); + break; + } +} + +/** + * \brief Set gpio pin function + */ +static inline void _gpio_set_pin_function(const uint32_t gpio, const uint32_t function) +{ + uint8_t port = GPIO_PORT(gpio); + uint8_t pin = GPIO_PIN(gpio); + + if (function == GPIO_PIN_FUNCTION_OFF) { + hri_port_write_PINCFG_PMUXEN_bit(PORT, port, pin, false); + + } else { + hri_port_write_PINCFG_PMUXEN_bit(PORT, port, pin, true); + + if (pin & 1) { + // Odd numbered pin + hri_port_write_PMUX_PMUXO_bf(PORT, port, pin >> 1, function & 0xffff); + } else { + // Even numbered pin + hri_port_write_PMUX_PMUXE_bf(PORT, port, pin >> 1, function & 0xffff); + } + } +} diff --git a/atmel-samd/asf4/samd51/hpl/ramecc/hpl_ramecc.c b/atmel-samd/asf4/samd51/hpl/ramecc/hpl_ramecc.c new file mode 100644 index 000000000..c4daec518 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/ramecc/hpl_ramecc.c @@ -0,0 +1,93 @@ +/** + * \file + * + * \brief Generic RAMECC related functionality. + * + * Copyright (C) 2016 -2107 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <utils.h> +#include <utils_assert.h> +#include <hpl_ramecc.h> + +/* RAMECC device descriptor */ +struct _ramecc_device device; + +/** + * \brief Initialize RAMECC + */ +int32_t _ramecc_init(void) +{ + if (hri_ramecc_get_STATUS_ECCDIS_bit(RAMECC)) { + return ERR_ABORTED; + } + + NVIC_DisableIRQ(RAMECC_IRQn); + NVIC_ClearPendingIRQ(RAMECC_IRQn); + NVIC_EnableIRQ(RAMECC_IRQn); + + return ERR_NONE; +} + +void _ramecc_register_callback(const enum _ramecc_callback_type type, ramecc_cb_t cb) +{ + if (RAMECC_DUAL_ERROR_CB == type) { + device.ramecc_cb.dual_bit_err = cb; + hri_ramecc_write_INTEN_DUALE_bit(RAMECC, NULL != cb); + } else if (RAMECC_SINGLE_ERROR_CB == type) { + device.ramecc_cb.single_bit_err = cb; + hri_ramecc_write_INTEN_SINGLEE_bit(RAMECC, NULL != cb); + } +} + +/** + * \internal RAMECC interrupt handler + */ +void RAMECC_Handler(void) +{ + struct _ramecc_device *dev = (struct _ramecc_device *)&device; + volatile uint32_t int_mask = hri_ramecc_read_INTFLAG_reg(RAMECC); + + if (int_mask & RAMECC_INTFLAG_DUALE && dev->ramecc_cb.dual_bit_err) { + dev->ramecc_cb.dual_bit_err((uint32_t)hri_ramecc_read_ERRADDR_reg(RAMECC)); + } else if (int_mask & RAMECC_INTFLAG_SINGLEE && dev->ramecc_cb.single_bit_err) { + dev->ramecc_cb.single_bit_err((uint32_t)hri_ramecc_read_ERRADDR_reg(RAMECC)); + } else { + return; + } +} diff --git a/atmel-samd/asf4/samd51/hpl/rtc/hpl_rtc.c b/atmel-samd/asf4/samd51/hpl/rtc/hpl_rtc.c new file mode 100644 index 000000000..6ef8a96ee --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/rtc/hpl_rtc.c @@ -0,0 +1,193 @@ + +/** + * \file + * + * \brief RTC Driver + * + * Copyright (C) 2014 - 2017 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_calendar.h> +#include <hpl_timer.h> +#include <utils_assert.h> +#include <hpl_rtc_config.h> + +/*!< Pointer to hpl device */ + +static struct _timer_device *_rtc_dev = NULL; + +/** + * \brief Initialize Timer + */ +int32_t _timer_init(struct _timer_device *const dev, void *const hw) +{ + ASSERT(dev); + + dev->hw = hw; + + hri_rtcmode0_write_CTRLA_reg(dev->hw, RTC_MODE0_CTRLA_SWRST); + hri_rtcmode0_wait_for_sync(dev->hw, RTC_MODE0_SYNCBUSY_SWRST); + +#if CONF_RTC_EVENT_CONTROL_ENABLE == 1 + hri_rtcmode0_write_EVCTRL_reg(dev->hw, + (CONF_RTC_PEREO0 << RTC_MODE0_EVCTRL_PEREO0_Pos) + | (CONF_RTC_PEREO1 << RTC_MODE0_EVCTRL_PEREO1_Pos) + | (CONF_RTC_PEREO2 << RTC_MODE0_EVCTRL_PEREO2_Pos) + | (CONF_RTC_PEREO3 << RTC_MODE0_EVCTRL_PEREO3_Pos) + | (CONF_RTC_PEREO4 << RTC_MODE0_EVCTRL_PEREO4_Pos) + | (CONF_RTC_PEREO5 << RTC_MODE0_EVCTRL_PEREO5_Pos) + | (CONF_RTC_PEREO6 << RTC_MODE0_EVCTRL_PEREO6_Pos) + | (CONF_RTC_PEREO7 << RTC_MODE0_EVCTRL_PEREO7_Pos) + | (CONF_RTC_COMPE0 << RTC_MODE0_EVCTRL_CMPEO_Pos) + | (CONF_RTC_COMPE1 << RTC_MODE0_EVCTRL_CMPEO1_Pos) + | (CONF_RTC_TAMPEREO << RTC_MODE0_EVCTRL_TAMPEREO_Pos) + | (CONF_RTC_TAMPEVEI << RTC_MODE0_EVCTRL_TAMPEVEI_Pos) + | (CONF_RTC_OVFEO << RTC_MODE0_EVCTRL_OVFEO_Pos)); +#endif + + hri_rtcmode0_write_CTRLA_reg( + dev->hw, RTC_MODE0_CTRLA_PRESCALER(CONF_RTC_PRESCALER) | RTC_MODE0_CTRLA_COUNTSYNC | RTC_MODE0_CTRLA_MATCHCLR); + hri_rtcmode0_write_COMP_reg(dev->hw, 0, CONF_RTC_COMP_VAL); + hri_rtcmode0_set_INTEN_CMP0_bit(dev->hw); + + _rtc_dev = dev; + + return ERR_NONE; +} + +/** + * \brief De-initialize Timer + */ +void _timer_deinit(struct _timer_device *const dev) +{ + ASSERT(dev && dev->hw); + + NVIC_DisableIRQ(RTC_IRQn); + + hri_rtcmode0_write_CTRLA_reg(dev->hw, RTC_MODE0_CTRLA_SWRST); +} + +/** + * \brief Start hardware timer + */ +void _timer_start(struct _timer_device *const dev) +{ + ASSERT(dev && dev->hw); + + NVIC_EnableIRQ(RTC_IRQn); + hri_rtcmode0_write_COUNT_reg(dev->hw, 0); + hri_rtcmode0_wait_for_sync(dev->hw, RTC_MODE0_SYNCBUSY_COUNT); + hri_rtcmode0_set_CTRLA_ENABLE_bit(dev->hw); +} + +/** + * \brief Stop hardware timer + */ +void _timer_stop(struct _timer_device *const dev) +{ + ASSERT(dev && dev->hw); + + hri_rtcmode0_clear_CTRLA_ENABLE_bit(dev->hw); +} + +/** + * \brief Set timer period + */ +void _timer_set_period(struct _timer_device *const dev, const uint32_t clock_cycles) +{ + hri_rtcmode0_write_COMP_reg(dev->hw, 0, clock_cycles); +} + +/** + * \brief Retrieve timer period + */ +uint32_t _timer_get_period(const struct _timer_device *const dev) +{ + return hri_rtcmode0_read_COMP_reg(dev->hw, 0); +} + +/** + * \brief Check if timer is running + */ +bool _timer_is_started(const struct _timer_device *const dev) +{ + return hri_rtcmode0_get_CTRLA_ENABLE_bit(dev->hw); +} + +/** + * \brief Set timer IRQ + */ +void _timer_set_irq(struct _timer_device *const dev) +{ + (void)dev; +} + +/** + * \brief RTC Timer interrupt handler + * + * \param[in] p The pointer to calendar device struct + */ +static void _rtc_timer_interrupt_handler(struct _timer_device *dev) +{ + /* Read and mask interrupt flag register */ + uint16_t flag = hri_rtcmode0_read_INTFLAG_reg(dev->hw); + + if (flag & RTC_MODE0_INTFLAG_CMP0) { + if (dev->timer_cb.period_expired) { + dev->timer_cb.period_expired(dev); + } + /* Clear interrupt flag */ + hri_rtcmode0_clear_interrupt_CMP0_bit(dev->hw); + } +} + +/** + * \brief Retrieve timer helper functions + */ +struct _timer_hpl_interface *_rtc_get_timer(void) +{ + return NULL; +} + +/** +* \brief Rtc interrupt handler +*/ +void RTC_Handler(void) +{ + _rtc_timer_interrupt_handler(_rtc_dev); +} diff --git a/atmel-samd/asf4/samd51/hpl/rtc/hpl_rtc_base.h b/atmel-samd/asf4/samd51/hpl/rtc/hpl_rtc_base.h new file mode 100644 index 000000000..bbcf242b9 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/rtc/hpl_rtc_base.h @@ -0,0 +1,62 @@ +/** + * \file + * + * \brief RTC + * + * Copyright (C) 2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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 distributionn. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + */ + +#ifndef _HPL_RTC2_V200_H_INCLUDED +#define _HPL_RTC2_V200_H_INCLUDED + +#include <hpl_timer.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Retrieve timer helper functions + * + * \return A pointer to set of timer helper functions + */ +struct _timer_hpl_interface *_rtc_get_timer(void); + +#ifdef __cplusplus +} +#endif +#endif /* _HPL_RTC2_V200_H_INCLUDED */ diff --git a/atmel-samd/asf4/samd51/hpl/sercom/hpl_sercom.c b/atmel-samd/asf4/samd51/hpl/sercom/hpl_sercom.c new file mode 100644 index 000000000..9fe612f72 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/sercom/hpl_sercom.c @@ -0,0 +1,2922 @@ + +/** + * \file + * + * \brief SAM Serial Communication Interface + * + * Copyright (C) 2014-2017 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ +#include <hpl_dma.h> +#include <hpl_i2c_m_async.h> +#include <hpl_i2c_m_sync.h> +#include <hpl_i2c_s_async.h> +#include <hpl_sercom_config.h> +#include <hpl_spi_m_async.h> +#include <hpl_spi_m_sync.h> +#include <hpl_spi_s_async.h> +#include <hpl_spi_s_sync.h> +#include <hpl_usart_async.h> +#include <hpl_usart_sync.h> +#include <utils.h> +#include <utils_assert.h> + +#ifndef CONF_SERCOM_0_USART_ENABLE +#define CONF_SERCOM_0_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_1_USART_ENABLE +#define CONF_SERCOM_1_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_2_USART_ENABLE +#define CONF_SERCOM_2_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_3_USART_ENABLE +#define CONF_SERCOM_3_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_4_USART_ENABLE +#define CONF_SERCOM_4_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_5_USART_ENABLE +#define CONF_SERCOM_5_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_6_USART_ENABLE +#define CONF_SERCOM_6_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_7_USART_ENABLE +#define CONF_SERCOM_7_USART_ENABLE 0 +#endif + +/** Amount of SERCOM that is used as USART. */ +#define SERCOM_USART_AMOUNT \ + (CONF_SERCOM_0_USART_ENABLE + CONF_SERCOM_1_USART_ENABLE + CONF_SERCOM_2_USART_ENABLE + CONF_SERCOM_3_USART_ENABLE \ + + CONF_SERCOM_4_USART_ENABLE \ + + CONF_SERCOM_5_USART_ENABLE \ + + CONF_SERCOM_6_USART_ENABLE \ + + CONF_SERCOM_7_USART_ENABLE) + +/** + * \brief Macro is used to fill usart configuration structure based on + * its number + * + * \param[in] n The number of structures + */ +#define SERCOM_CONFIGURATION(n) \ + { \ + n, SERCOM_USART_CTRLA_MODE(CONF_SERCOM_##n##_USART_MODE) \ + | (CONF_SERCOM_##n##_USART_RUNSTDBY << SERCOM_USART_CTRLA_RUNSTDBY_Pos) \ + | (CONF_SERCOM_##n##_USART_IBON << SERCOM_USART_CTRLA_IBON_Pos) \ + | (CONF_SERCOM_##n##_USART_TXINV << SERCOM_USART_CTRLA_TXINV_Pos) \ + | (CONF_SERCOM_##n##_USART_RXINV << SERCOM_USART_CTRLA_RXINV_Pos) \ + | SERCOM_USART_CTRLA_SAMPR(CONF_SERCOM_##n##_USART_SAMPR) \ + | SERCOM_USART_CTRLA_TXPO(CONF_SERCOM_##n##_USART_TXPO) \ + | SERCOM_USART_CTRLA_RXPO(CONF_SERCOM_##n##_USART_RXPO) \ + | SERCOM_USART_CTRLA_SAMPA(CONF_SERCOM_##n##_USART_SAMPA) \ + | SERCOM_USART_CTRLA_FORM(CONF_SERCOM_##n##_USART_FORM) \ + | (CONF_SERCOM_##n##_USART_CMODE << SERCOM_USART_CTRLA_CMODE_Pos) \ + | (CONF_SERCOM_##n##_USART_CPOL << SERCOM_USART_CTRLA_CPOL_Pos) \ + | (CONF_SERCOM_##n##_USART_DORD << SERCOM_USART_CTRLA_DORD_Pos), \ + SERCOM_USART_CTRLB_CHSIZE(CONF_SERCOM_##n##_USART_CHSIZE) \ + | (CONF_SERCOM_##n##_USART_SBMODE << SERCOM_USART_CTRLB_SBMODE_Pos) \ + | (CONF_SERCOM_##n##_USART_CLODEN << SERCOM_USART_CTRLB_COLDEN_Pos) \ + | (CONF_SERCOM_##n##_USART_SFDE << SERCOM_USART_CTRLB_SFDE_Pos) \ + | (CONF_SERCOM_##n##_USART_ENC << SERCOM_USART_CTRLB_ENC_Pos) \ + | (CONF_SERCOM_##n##_USART_PMODE << SERCOM_USART_CTRLB_PMODE_Pos) \ + | (CONF_SERCOM_##n##_USART_TXEN << SERCOM_USART_CTRLB_TXEN_Pos) \ + | (CONF_SERCOM_##n##_USART_RXEN << SERCOM_USART_CTRLB_RXEN_Pos), \ + SERCOM_USART_CTRLC_GTIME(CONF_SERCOM_##n##_USART_GTIME) \ + | (CONF_SERCOM_##n##_USART_DSNACK << SERCOM_USART_CTRLC_DSNACK_Pos) \ + | (CONF_SERCOM_##n##_USART_INACK << SERCOM_USART_CTRLC_INACK_Pos) \ + | SERCOM_USART_CTRLC_MAXITER(CONF_SERCOM_##n##_USART_MAXITER), \ + (uint16_t)(CONF_SERCOM_##n##_USART_BAUD_RATE), CONF_SERCOM_##n##_USART_FRACTIONAL, \ + CONF_SERCOM_##n##_USART_RECEIVE_PULSE_LENGTH, CONF_SERCOM_##n##_USART_DEBUG_STOP_MODE, \ + } + +/** + * \brief SERCOM USART configuration type + */ +struct usart_configuration { + uint8_t number; + hri_sercomusart_ctrla_reg_t ctrl_a; + hri_sercomusart_ctrlb_reg_t ctrl_b; + hri_sercomusart_ctrlc_reg_t ctrl_c; + hri_sercomusart_baud_reg_t baud; + uint8_t fractional; + hri_sercomusart_rxpl_reg_t rxpl; + hri_sercomusart_dbgctrl_reg_t debug_ctrl; +}; + +#if SERCOM_USART_AMOUNT < 1 +/** Dummy array to pass compiling. */ +static struct usart_configuration _usarts[1] = {{0}}; +#else +/** + * \brief Array of SERCOM USART configurations + */ +static struct usart_configuration _usarts[] = { +#if CONF_SERCOM_0_USART_ENABLE == 1 + SERCOM_CONFIGURATION(0), +#endif +#if CONF_SERCOM_1_USART_ENABLE == 1 + SERCOM_CONFIGURATION(1), +#endif +#if CONF_SERCOM_2_USART_ENABLE == 1 + SERCOM_CONFIGURATION(2), +#endif +#if CONF_SERCOM_3_USART_ENABLE == 1 + SERCOM_CONFIGURATION(3), +#endif +#if CONF_SERCOM_4_USART_ENABLE == 1 + SERCOM_CONFIGURATION(4), +#endif +#if CONF_SERCOM_5_USART_ENABLE == 1 + SERCOM_CONFIGURATION(5), +#endif +#if CONF_SERCOM_6_USART_ENABLE == 1 + SERCOM_CONFIGURATION(6), +#endif +#if CONF_SERCOM_7_USART_ENABLE == 1 + SERCOM_CONFIGURATION(7), +#endif +}; +#endif + +static uint8_t _get_sercom_index(const void *const hw); +static uint8_t _sercom_get_irq_num(const void *const hw); +static void _sercom_init_irq_param(const void *const hw, void *dev); +static uint8_t _sercom_get_hardware_index(const void *const hw); + +static int32_t _usart_init(void *const hw); +static inline void _usart_deinit(void *const hw); +static uint16_t _usart_calculate_baud_rate(const uint32_t baud, const uint32_t clock_rate, const uint8_t samples, + const enum usart_baud_rate_mode mode, const uint8_t fraction); +static void _usart_set_baud_rate(void *const hw, const uint32_t baud_rate); +static void _usart_set_data_order(void *const hw, const enum usart_data_order order); +static void _usart_set_mode(void *const hw, const enum usart_mode mode); +static void _usart_set_parity(void *const hw, const enum usart_parity parity); +static void _usart_set_stop_bits(void *const hw, const enum usart_stop_bits stop_bits); +static void _usart_set_character_size(void *const hw, const enum usart_character_size size); + +/** + * \brief Initialize synchronous SERCOM USART + */ +int32_t _usart_sync_init(struct _usart_sync_device *const device, void *const hw) +{ + ASSERT(device); + + device->hw = hw; + + return _usart_init(hw); +} + +/** + * \brief Initialize asynchronous SERCOM USART + */ +int32_t _usart_async_init(struct _usart_async_device *const device, void *const hw) +{ + int32_t init_status; + + ASSERT(device); + + init_status = _usart_init(hw); + if (init_status) { + return init_status; + } + device->hw = hw; + _sercom_init_irq_param(hw, (void *)device); + uint8_t irq = _sercom_get_irq_num(hw); + for (uint32_t i = 0; i < 4; i++) { + NVIC_DisableIRQ((IRQn_Type)irq); + NVIC_ClearPendingIRQ((IRQn_Type)irq); + NVIC_EnableIRQ((IRQn_Type)irq); + irq++; + } + + return ERR_NONE; +} + +/** + * \brief De-initialize SERCOM USART + */ +void _usart_sync_deinit(struct _usart_sync_device *const device) +{ + _usart_deinit(device->hw); +} + +/** + * \brief De-initialize SERCOM USART + */ +void _usart_async_deinit(struct _usart_async_device *const device) +{ + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(device->hw)); + _usart_deinit(device->hw); +} + +/** + * \brief Calculate baud rate register value + */ +uint16_t _usart_sync_calculate_baud_rate(const uint32_t baud, const uint32_t clock_rate, const uint8_t samples, + const enum usart_baud_rate_mode mode, const uint8_t fraction) +{ + return _usart_calculate_baud_rate(baud, clock_rate, samples, mode, fraction); +} + +/** + * \brief Calculate baud rate register value + */ +uint16_t _usart_async_calculate_baud_rate(const uint32_t baud, const uint32_t clock_rate, const uint8_t samples, + const enum usart_baud_rate_mode mode, const uint8_t fraction) +{ + return _usart_calculate_baud_rate(baud, clock_rate, samples, mode, fraction); +} + +/** + * \brief Enable SERCOM module + */ +void _usart_sync_enable(struct _usart_sync_device *const device) +{ + hri_sercomusart_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Enable SERCOM module + */ +void _usart_async_enable(struct _usart_async_device *const device) +{ + hri_sercomusart_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable SERCOM module + */ +void _usart_sync_disable(struct _usart_sync_device *const device) +{ + hri_sercomusart_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable SERCOM module + */ +void _usart_async_disable(struct _usart_async_device *const device) +{ + hri_sercomusart_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Set baud rate + */ +void _usart_sync_set_baud_rate(struct _usart_sync_device *const device, const uint32_t baud_rate) +{ + _usart_set_baud_rate(device->hw, baud_rate); +} + +/** + * \brief Set baud rate + */ +void _usart_async_set_baud_rate(struct _usart_async_device *const device, const uint32_t baud_rate) +{ + _usart_set_baud_rate(device->hw, baud_rate); +} + +/** + * \brief Set data order + */ +void _usart_sync_set_data_order(struct _usart_sync_device *const device, const enum usart_data_order order) +{ + _usart_set_data_order(device->hw, order); +} + +/** + * \brief Set data order + */ +void _usart_async_set_data_order(struct _usart_async_device *const device, const enum usart_data_order order) +{ + _usart_set_data_order(device->hw, order); +} + +/** + * \brief Set mode + */ +void _usart_sync_set_mode(struct _usart_sync_device *const device, const enum usart_mode mode) +{ + _usart_set_mode(device->hw, mode); +} + +/** + * \brief Set mode + */ +void _usart_async_set_mode(struct _usart_async_device *const device, const enum usart_mode mode) +{ + _usart_set_mode(device->hw, mode); +} + +/** + * \brief Set parity + */ +void _usart_sync_set_parity(struct _usart_sync_device *const device, const enum usart_parity parity) +{ + _usart_set_parity(device->hw, parity); +} + +/** + * \brief Set parity + */ +void _usart_async_set_parity(struct _usart_async_device *const device, const enum usart_parity parity) +{ + _usart_set_parity(device->hw, parity); +} + +/** + * \brief Set stop bits mode + */ +void _usart_sync_set_stop_bits(struct _usart_sync_device *const device, const enum usart_stop_bits stop_bits) +{ + _usart_set_stop_bits(device->hw, stop_bits); +} + +/** + * \brief Set stop bits mode + */ +void _usart_async_set_stop_bits(struct _usart_async_device *const device, const enum usart_stop_bits stop_bits) +{ + _usart_set_stop_bits(device->hw, stop_bits); +} + +/** + * \brief Set character size + */ +void _usart_sync_set_character_size(struct _usart_sync_device *const device, const enum usart_character_size size) +{ + _usart_set_character_size(device->hw, size); +} + +/** + * \brief Set character size + */ +void _usart_async_set_character_size(struct _usart_async_device *const device, const enum usart_character_size size) +{ + _usart_set_character_size(device->hw, size); +} + +/** + * \brief Retrieve SERCOM usart status + */ +uint32_t _usart_sync_get_status(const struct _usart_sync_device *const device) +{ + return hri_sercomusart_read_STATUS_reg(device->hw); +} + +/** + * \brief Retrieve SERCOM usart status + */ +uint32_t _usart_async_get_status(const struct _usart_async_device *const device) +{ + return hri_sercomusart_read_STATUS_reg(device->hw); +} + +/** + * \brief Write a byte to the given SERCOM USART instance + */ +void _usart_sync_write_byte(struct _usart_sync_device *const device, uint8_t data) +{ + hri_sercomusart_write_DATA_reg(device->hw, data); +} + +/** + * \brief Write a byte to the given SERCOM USART instance + */ +void _usart_async_write_byte(struct _usart_async_device *const device, uint8_t data) +{ + hri_sercomusart_write_DATA_reg(device->hw, data); +} + +/** + * \brief Read a byte from the given SERCOM USART instance + */ +uint8_t _usart_sync_read_byte(const struct _usart_sync_device *const device) +{ + return hri_sercomusart_read_DATA_reg(device->hw); +} + +/** + * \brief Check if USART is ready to send next byte + */ +bool _usart_sync_is_byte_sent(const struct _usart_sync_device *const device) +{ + return hri_sercomusart_get_interrupt_DRE_bit(device->hw); +} + +/** + * \brief Check if USART is ready to send next byte + */ +bool _usart_async_is_byte_sent(const struct _usart_async_device *const device) +{ + return hri_sercomusart_get_interrupt_DRE_bit(device->hw); +} + +/** + * \brief Check if there is data received by USART + */ +bool _usart_sync_is_byte_received(const struct _usart_sync_device *const device) +{ + return hri_sercomusart_get_interrupt_RXC_bit(device->hw); +} + +/** + * \brief Set the state of flow control pins + */ +void _usart_sync_set_flow_control_state(struct _usart_sync_device *const device, + const union usart_flow_control_state state) +{ + (void)device; + (void)state; +} + +/** + * \brief Set the state of flow control pins + */ +void _usart_async_set_flow_control_state(struct _usart_async_device *const device, + const union usart_flow_control_state state) +{ + (void)device; + (void)state; +} + +/** + * \brief Retrieve the state of flow control pins + */ +union usart_flow_control_state _usart_sync_get_flow_control_state(const struct _usart_sync_device *const device) +{ + (void)device; + union usart_flow_control_state state; + + state.value = 0; + state.bit.unavailable = 1; + return state; +} + +/** + * \brief Retrieve the state of flow control pins + */ +union usart_flow_control_state _usart_async_get_flow_control_state(const struct _usart_async_device *const device) +{ + (void)device; + union usart_flow_control_state state; + + state.value = 0; + state.bit.unavailable = 1; + return state; +} + +/** + * \brief Enable data register empty interrupt + */ +void _usart_async_enable_byte_sent_irq(struct _usart_async_device *const device) +{ + hri_sercomusart_set_INTEN_DRE_bit(device->hw); +} + +/** + * \brief Enable transmission complete interrupt + */ +void _usart_async_enable_tx_done_irq(struct _usart_async_device *const device) +{ + hri_sercomusart_set_INTEN_TXC_bit(device->hw); +} + +/** + * \brief Retrieve ordinal number of the given sercom hardware instance + */ +static uint8_t _sercom_get_hardware_index(const void *const hw) +{ + Sercom *const sercom_modules[] = SERCOM_INSTS; + /* Find index for SERCOM instance. */ + for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) { + if ((uint32_t)hw == (uint32_t)sercom_modules[i]) { + return i; + } + } + return 0; +} + +/** + * \brief Retrieve ordinal number of the given SERCOM USART hardware instance + */ +uint8_t _usart_sync_get_hardware_index(const struct _usart_sync_device *const device) +{ + return _sercom_get_hardware_index(device->hw); +} + +/** + * \brief Retrieve ordinal number of the given SERCOM USART hardware instance + */ +uint8_t _usart_async_get_hardware_index(const struct _usart_async_device *const device) +{ + return _sercom_get_hardware_index(device->hw); +} + +/** + * \brief Enable/disable USART interrupt + */ +void _usart_async_set_irq_state(struct _usart_async_device *const device, const enum _usart_async_callback_type type, + const bool state) +{ + ASSERT(device); + + if (USART_ASYNC_BYTE_SENT == type || USART_ASYNC_TX_DONE == type) { + hri_sercomusart_write_INTEN_DRE_bit(device->hw, state); + hri_sercomusart_write_INTEN_TXC_bit(device->hw, state); + } else if (USART_ASYNC_RX_DONE == type) { + hri_sercomusart_write_INTEN_RXC_bit(device->hw, state); + } else if (USART_ASYNC_ERROR == type) { + hri_sercomusart_write_INTEN_ERROR_bit(device->hw, state); + } +} + +/** + * \internal Retrieve ordinal number of the given sercom hardware instance + * + * \param[in] hw The pointer to hardware instance + + * \return The ordinal number of the given sercom hardware instance + */ +static uint8_t _get_sercom_index(const void *const hw) +{ + uint8_t sercom_offset = _sercom_get_hardware_index(hw); + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_usarts); i++) { + if (_usarts[i].number == sercom_offset) { + return i; + } + } + + ASSERT(false); + return 0; +} + +/** + * \brief Init irq param with the given sercom hardware instance + */ +static void _sercom_init_irq_param(const void *const hw, void *dev) +{ +} + +/** + * \internal Initialize SERCOM USART + * + * \param[in] hw The pointer to hardware instance + * + * \return The status of initialization + */ +static int32_t _usart_init(void *const hw) +{ + uint8_t i = _get_sercom_index(hw); + + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_SWRST); + if (hri_sercomusart_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + hri_sercomusart_set_CTRLA_SWRST_bit(hw); + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_SWRST); + + hri_sercomusart_write_CTRLA_reg(hw, _usarts[i].ctrl_a); + hri_sercomusart_write_CTRLB_reg(hw, _usarts[i].ctrl_b); + hri_sercomusart_write_CTRLC_reg(hw, _usarts[i].ctrl_c); + if ((_usarts[i].ctrl_a & SERCOM_USART_CTRLA_SAMPR(0x1)) || (_usarts[i].ctrl_a & SERCOM_USART_CTRLA_SAMPR(0x3))) { + ((Sercom *)hw)->USART.BAUD.FRAC.BAUD = _usarts[i].baud; + ((Sercom *)hw)->USART.BAUD.FRAC.FP = _usarts[i].fractional; + } else { + hri_sercomusart_write_BAUD_reg(hw, _usarts[i].baud); + } + + hri_sercomusart_write_RXPL_reg(hw, _usarts[i].rxpl); + hri_sercomusart_write_DBGCTRL_reg(hw, _usarts[i].debug_ctrl); + + return ERR_NONE; +} + +/** + * \internal De-initialize SERCOM USART + * + * \param[in] hw The pointer to hardware instance + */ +static inline void _usart_deinit(void *const hw) +{ + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + hri_sercomusart_set_CTRLA_SWRST_bit(hw); +} + +/** + * \internal Calculate baud rate register value + * + * \param[in] baud Required baud rate + * \param[in] clock_rate SERCOM clock frequency + * \param[in] samples The number of samples + * \param[in] mode USART mode + * \param[in] fraction A fraction value + * + * \return Calculated baud rate register value + */ +static uint16_t _usart_calculate_baud_rate(const uint32_t baud, const uint32_t clock_rate, const uint8_t samples, + const enum usart_baud_rate_mode mode, const uint8_t fraction) +{ + if (USART_BAUDRATE_ASYNCH_ARITHMETIC == mode) { + return 65536 - ((uint64_t)65536 * samples * baud) / clock_rate; + } + + if (USART_BAUDRATE_ASYNCH_FRACTIONAL == mode) { + return clock_rate / baud / samples + SERCOM_USART_BAUD_FRACFP_FP(fraction); + } + + if (USART_BAUDRATE_SYNCH == mode) { + return clock_rate / baud / 2 - 1; + } + + return 0; +} + +/** + * \internal Set baud rate + * + * \param[in] device The pointer to USART device instance + * \param[in] baud_rate A baud rate to set + */ +static void _usart_set_baud_rate(void *const hw, const uint32_t baud_rate) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_BAUD_reg(hw, baud_rate); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set data order + * + * \param[in] device The pointer to USART device instance + * \param[in] order A data order to set + */ +static void _usart_set_data_order(void *const hw, const enum usart_data_order order) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_CTRLA_DORD_bit(hw, order); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set mode + * + * \param[in] device The pointer to USART device instance + * \param[in] mode A mode to set + */ +static void _usart_set_mode(void *const hw, const enum usart_mode mode) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_CTRLA_CMODE_bit(hw, mode); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set parity + * + * \param[in] device The pointer to USART device instance + * \param[in] parity A parity to set + */ +static void _usart_set_parity(void *const hw, const enum usart_parity parity) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + + if (USART_PARITY_NONE != parity) { + hri_sercomusart_set_CTRLA_FORM_bf(hw, 1); + } else { + hri_sercomusart_clear_CTRLA_FORM_bf(hw, 1); + } + + hri_sercomusart_write_CTRLB_PMODE_bit(hw, parity); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set stop bits mode + * + * \param[in] device The pointer to USART device instance + * \param[in] stop_bits A stop bits mode to set + */ +static void _usart_set_stop_bits(void *const hw, const enum usart_stop_bits stop_bits) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_CTRLB_SBMODE_bit(hw, stop_bits); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set character size + * + * \param[in] device The pointer to USART device instance + * \param[in] size A character size to set + */ +static void _usart_set_character_size(void *const hw, const enum usart_character_size size) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_CTRLB_CHSIZE_bf(hw, size); + CRITICAL_SECTION_LEAVE() + + if (enabled) { + hri_sercomusart_set_CTRLA_ENABLE_bit(hw); + } +} + +/* Sercom I2C implementation */ + +#ifndef CONF_SERCOM_0_I2CM_ENABLE +#define CONF_SERCOM_0_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_1_I2CM_ENABLE +#define CONF_SERCOM_1_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_2_I2CM_ENABLE +#define CONF_SERCOM_2_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_3_I2CM_ENABLE +#define CONF_SERCOM_3_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_4_I2CM_ENABLE +#define CONF_SERCOM_4_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_5_I2CM_ENABLE +#define CONF_SERCOM_5_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_6_I2CM_ENABLE +#define CONF_SERCOM_6_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_7_I2CM_ENABLE +#define CONF_SERCOM_7_I2CM_ENABLE 0 +#endif + +/** Amount of SERCOM that is used as I2C Master. */ +#define SERCOM_I2CM_AMOUNT \ + (CONF_SERCOM_0_I2CM_ENABLE + CONF_SERCOM_1_I2CM_ENABLE + CONF_SERCOM_2_I2CM_ENABLE + CONF_SERCOM_3_I2CM_ENABLE \ + + CONF_SERCOM_4_I2CM_ENABLE \ + + CONF_SERCOM_5_I2CM_ENABLE \ + + CONF_SERCOM_6_I2CM_ENABLE \ + + CONF_SERCOM_7_I2CM_ENABLE) + +/** + * \brief Macro is used to fill i2cm configuration structure based on + * its number + * + * \param[in] n The number of structures + */ +#define I2CM_CONFIGURATION(n) \ + { \ + (n), (SERCOM_I2CM_CTRLA_MODE_I2C_MASTER) | (CONF_SERCOM_##n##_I2CM_RUNSTDBY << SERCOM_I2CM_CTRLA_RUNSTDBY_Pos) \ + | (CONF_SERCOM_##n##_I2CM_SPEED << SERCOM_I2CM_CTRLA_SPEED_Pos) \ + | (CONF_SERCOM_##n##_I2CM_MEXTTOEN << SERCOM_I2CM_CTRLA_MEXTTOEN_Pos) \ + | (CONF_SERCOM_##n##_I2CM_SEXTTOEN << SERCOM_I2CM_CTRLA_SEXTTOEN_Pos) \ + | (CONF_SERCOM_##n##_I2CM_INACTOUT << SERCOM_I2CM_CTRLA_INACTOUT_Pos) \ + | (CONF_SERCOM_##n##_I2CM_LOWTOUT << SERCOM_I2CM_CTRLA_LOWTOUTEN_Pos) \ + | (CONF_SERCOM_##n##_I2CM_SDAHOLD << SERCOM_I2CM_CTRLA_SDAHOLD_Pos), \ + SERCOM_I2CM_CTRLB_SMEN, (uint32_t)(CONF_SERCOM_##n##_I2CM_BAUD_RATE), \ + CONF_SERCOM_##n##_I2CM_DEBUG_STOP_MODE, CONF_SERCOM_##n##_I2CM_TRISE, CONF_GCLK_SERCOM##n##_CORE_FREQUENCY \ + } + +#define ERROR_FLAG (1 << 7) +#define SB_FLAG (1 << 1) +#define MB_FLAG (1 << 0) + +#define CMD_STOP 0x3 +#define I2C_IDLE 0x1 +#define I2C_SM 0x0 +#define I2C_FM 0x1 +#define I2C_HS 0x2 +#define TEN_ADDR_FRAME 0x78 +#define TEN_ADDR_MASK 0x3ff +#define SEVEN_ADDR_MASK 0x7f + +/** + * \brief SERCOM I2CM configuration type + */ +struct i2cm_configuration { + uint8_t number; + hri_sercomi2cm_ctrla_reg_t ctrl_a; + hri_sercomi2cm_ctrlb_reg_t ctrl_b; + hri_sercomi2cm_baud_reg_t baud; + hri_sercomi2cm_dbgctrl_reg_t dbgctrl; + uint16_t trise; + uint32_t clk; /* SERCOM peripheral clock frequency */ +}; + +static inline void _i2c_m_enable_implementation(void *hw); +static int32_t _i2c_m_sync_init_impl(struct _i2c_m_service *const service, void *const hw); + +#if SERCOM_I2CM_AMOUNT < 1 +/** Dummy array to pass compiling. */ +static struct i2cm_configuration _i2cms[1] = {{0}}; +#else +/** + * \brief Array of SERCOM I2CM configurations + */ +static struct i2cm_configuration _i2cms[] = { +#if CONF_SERCOM_0_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(0), +#endif +#if CONF_SERCOM_1_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(1), +#endif +#if CONF_SERCOM_2_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(2), +#endif +#if CONF_SERCOM_3_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(3), +#endif +#if CONF_SERCOM_4_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(4), +#endif +#if CONF_SERCOM_5_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(5), +#endif +#if CONF_SERCOM_6_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(6), +#endif +#if CONF_SERCOM_7_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(7), +#endif +}; +#endif + +/** + * \internal Retrieve ordinal number of the given sercom hardware instance + * + * \param[in] hw The pointer to hardware instance + + * \return The ordinal number of the given sercom hardware instance + */ +static int8_t _get_i2cm_index(const void *const hw) +{ + uint8_t sercom_offset = _sercom_get_hardware_index(hw); + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_i2cms); i++) { + if (_i2cms[i].number == sercom_offset) { + return i; + } + } + + ASSERT(false); + return -1; +} + +static inline void _sercom_i2c_send_stop(void *const hw) +{ + hri_sercomi2cm_set_CTRLB_CMD_bf(hw, CMD_STOP); +} + +/** + * \brief SERCOM I2CM analyze hardware status and transfer next byte + */ +static inline int32_t _sercom_i2c_sync_analyse_flags(void *const hw, uint32_t flags, struct _i2c_m_msg *const msg) +{ + int sclsm = hri_sercomi2cm_get_CTRLA_SCLSM_bit(hw); + uint16_t status = hri_sercomi2cm_read_STATUS_reg(hw); + + if (flags & MB_FLAG) { + /* tx error */ + if (status & SERCOM_I2CM_STATUS_ARBLOST) { + hri_sercomi2cm_clear_interrupt_MB_bit(hw); + msg->flags |= I2C_M_FAIL; + msg->flags &= ~I2C_M_BUSY; + + if (status & SERCOM_I2CM_STATUS_BUSERR) { + return I2C_ERR_BUS; + } + + return I2C_ERR_BAD_ADDRESS; + } else { + if (status & SERCOM_I2CM_STATUS_RXNACK) { + + /* Slave rejects to receive more data */ + if (msg->len > 0) { + msg->flags |= I2C_M_FAIL; + } + + if (msg->flags & I2C_M_STOP) { + _sercom_i2c_send_stop(hw); + } + + msg->flags &= ~I2C_M_BUSY; + + return I2C_NACK; + } + + if (msg->flags & I2C_M_TEN) { + hri_sercomi2cm_write_ADDR_reg(hw, + ((((msg->addr & TEN_ADDR_MASK) >> 8) | TEN_ADDR_FRAME) << 1) | I2C_M_RD + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + msg->flags &= ~I2C_M_TEN; + + return I2C_OK; + } + + if (msg->len == 0) { + if (msg->flags & I2C_M_STOP) { + _sercom_i2c_send_stop(hw); + } + + msg->flags &= ~I2C_M_BUSY; + } else { + hri_sercomi2cm_write_DATA_reg(hw, *msg->buffer); + msg->buffer++; + msg->len--; + } + + return I2C_OK; + } + } else if (flags & SB_FLAG) { + if ((msg->len) && !(status & SERCOM_I2CM_STATUS_RXNACK)) { + msg->len--; + + /* last byte, send nack */ + if ((msg->len == 0 && !sclsm) || (msg->len == 1 && sclsm)) { + hri_sercomi2cm_set_CTRLB_ACKACT_bit(hw); + } + + if (msg->len == 0) { + if (msg->flags & I2C_M_STOP) { + _sercom_i2c_send_stop(hw); + } + + msg->flags &= ~I2C_M_BUSY; + } + + /* Accessing DATA.DATA auto-triggers I2C bus operations. + * The operation performed depends on the state of + * CTRLB.ACKACT, CTRLB.SMEN + **/ + *msg->buffer++ = hri_sercomi2cm_read_DATA_reg(hw); + } else { + hri_sercomi2cm_clear_interrupt_SB_bit(hw); + return I2C_NACK; + } + + hri_sercomi2cm_clear_interrupt_SB_bit(hw); + } + + return I2C_OK; +} + +/** + * \brief Enable the i2c master module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_async_enable(struct _i2c_m_async_device *const i2c_dev) +{ + ASSERT(i2c_dev); + + _i2c_m_enable_implementation(i2c_dev->hw); + + return ERR_NONE; +} + +/** + * \brief Disable the i2c master module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_async_disable(struct _i2c_m_async_device *const i2c_dev) +{ + void *hw = i2c_dev->hw; + + ASSERT(i2c_dev); + ASSERT(i2c_dev->hw); + + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + hri_sercomi2cm_clear_CTRLA_ENABLE_bit(hw); + + return ERR_NONE; +} + +/** + * \brief Set baudrate of master + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] clkrate The clock rate of i2c master, in KHz + * \param[in] baudrate The baud rate desired for i2c master, in KHz + */ +int32_t _i2c_m_async_set_baudrate(struct _i2c_m_async_device *const i2c_dev, uint32_t clkrate, uint32_t baudrate) +{ + uint32_t tmp; + void * hw = i2c_dev->hw; + + if (hri_sercomi2cm_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + + tmp = _get_i2cm_index(hw); + clkrate = _i2cms[tmp].clk / 1000; + + if (i2c_dev->service.mode == I2C_STANDARD_MODE) { + tmp = (uint32_t)((clkrate - 10 * baudrate - baudrate * clkrate * (i2c_dev->service.trise * 0.000000001)) + / (2 * baudrate)); + hri_sercomi2cm_write_BAUD_BAUD_bf(hw, tmp); + } else if (i2c_dev->service.mode == I2C_FASTMODE) { + tmp = (uint32_t)((clkrate - 10 * baudrate - baudrate * clkrate * (i2c_dev->service.trise * 0.000000001)) + / (2 * baudrate)); + hri_sercomi2cm_write_BAUD_BAUD_bf(hw, tmp); + } else if (i2c_dev->service.mode == I2C_HIGHSPEED_MODE) { + tmp = (clkrate - 2 * baudrate) / (2 * baudrate); + hri_sercomi2cm_write_BAUD_HSBAUD_bf(hw, tmp); + } else { + /* error baudrate */ + return ERR_INVALID_ARG; + } + + return ERR_NONE; +} + +/** + * \brief Retrieve IRQ number for the given hardware instance + */ +static uint8_t _sercom_get_irq_num(const void *const hw) +{ + return SERCOM0_0_IRQn + (_sercom_get_hardware_index(hw) << 2); +} + +/** + * \brief Initialize sercom i2c module to use in async mode + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_async_init(struct _i2c_m_async_device *const i2c_dev, void *const hw) +{ + int32_t init_status; + + ASSERT(i2c_dev); + + i2c_dev->hw = hw; + + init_status = _i2c_m_sync_init_impl(&i2c_dev->service, hw); + if (init_status) { + return init_status; + } + + _sercom_init_irq_param(hw, (void *)i2c_dev); + uint8_t irq = _sercom_get_irq_num(hw); + for (uint32_t i = 0; i < 4; i++) { + NVIC_DisableIRQ((IRQn_Type)irq); + NVIC_ClearPendingIRQ((IRQn_Type)irq); + NVIC_EnableIRQ((IRQn_Type)irq); + irq++; + } + return ERR_NONE; +} + +/** + * \brief Deinitialize sercom i2c module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_async_deinit(struct _i2c_m_async_device *const i2c_dev) +{ + ASSERT(i2c_dev); + + hri_sercomi2cm_clear_CTRLA_ENABLE_bit(i2c_dev->hw); + hri_sercomi2cm_set_CTRLA_SWRST_bit(i2c_dev->hw); + + return ERR_NONE; +} + +/** + * \brief Transfer the slave address to bus, which will start the transfer + * + * \param[in] i2c_dev The pointer to i2c device + */ +static int32_t _sercom_i2c_send_address(struct _i2c_m_async_device *const i2c_dev) +{ + void * hw = i2c_dev->hw; + struct _i2c_m_msg *msg = &i2c_dev->service.msg; + int sclsm = hri_sercomi2cm_get_CTRLA_SCLSM_bit(hw); + + ASSERT(i2c_dev); + + if (msg->len == 1 && sclsm) { + hri_sercomi2cm_set_CTRLB_ACKACT_bit(hw); + } else { + hri_sercomi2cm_clear_CTRLB_ACKACT_bit(hw); + } + + /* ten bit address */ + if (msg->addr & I2C_M_TEN) { + if (msg->flags & I2C_M_RD) { + msg->flags |= I2C_M_TEN; + } + + hri_sercomi2cm_write_ADDR_reg(hw, + ((msg->addr & TEN_ADDR_MASK) << 1) | SERCOM_I2CM_ADDR_TENBITEN + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + } else { + hri_sercomi2cm_write_ADDR_reg(hw, + ((msg->addr & SEVEN_ADDR_MASK) << 1) | (msg->flags & I2C_M_RD ? I2C_M_RD : 0x0) + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + } + + return ERR_NONE; +} + +/** + * \brief Transfer data specified by msg + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] msg The pointer to i2c message + * + * \return Transfer status. + * \retval 0 Transfer success + * \retval <0 Transfer fail, return the error code + */ +int32_t _i2c_m_async_transfer(struct _i2c_m_async_device *i2c_dev, struct _i2c_m_msg *msg) +{ + int ret; + + ASSERT(i2c_dev); + ASSERT(i2c_dev->hw); + ASSERT(msg); + + if (msg->len == 0) { + return ERR_NONE; + } + + if (i2c_dev->service.msg.flags & I2C_M_BUSY) { + return ERR_BUSY; + } + + msg->flags |= I2C_M_BUSY; + i2c_dev->service.msg = *msg; + + ret = _sercom_i2c_send_address(i2c_dev); + + if (ret) { + i2c_dev->service.msg.flags &= ~I2C_M_BUSY; + + return ret; + } + + return ERR_NONE; +} + +/** + * \brief Set callback to be called in interrupt handler + * + * \param[in] i2c_dev The pointer to master i2c device + * \param[in] type The callback type + * \param[in] func The callback function pointer + */ +int32_t _i2c_m_async_register_callback(struct _i2c_m_async_device *const i2c_dev, enum _i2c_m_async_callback_type type, + FUNC_PTR func) +{ + switch (type) { + case I2C_M_ASYNC_DEVICE_ERROR: + i2c_dev->cb.error = (_i2c_error_cb_t)func; + break; + case I2C_M_ASYNC_DEVICE_TX_COMPLETE: + i2c_dev->cb.tx_complete = (_i2c_complete_cb_t)func; + break; + case I2C_M_ASYNC_DEVICE_RX_COMPLETE: + i2c_dev->cb.rx_complete = (_i2c_complete_cb_t)func; + break; + default: + /* error */ + break; + } + + return ERR_NONE; +} + +/** + * \brief Set stop condition on I2C + * + * \param i2c_dev Pointer to master i2c device + * + * \return Operation status + * \retval I2C_OK Operation was successfull + */ +int32_t _i2c_m_async_send_stop(struct _i2c_m_async_device *const i2c_dev) +{ + void *hw = i2c_dev->hw; + + _sercom_i2c_send_stop(hw); + + return I2C_OK; +} + +/** + * \brief Get number of bytes left in transfer buffer + * + * \param i2c_dev Pointer to i2c master device + * + * \return Bytes left in buffer + * \retval =>0 Bytes left in buffer + */ +int32_t _i2c_m_async_get_bytes_left(struct _i2c_m_async_device *const i2c_dev) +{ + if (i2c_dev->service.msg.flags & I2C_M_BUSY) { + return i2c_dev->service.msg.len; + } + + return 0; +} + +/** + * \brief Initialize sercom i2c module to use in sync mode + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_sync_init(struct _i2c_m_sync_device *const i2c_dev, void *const hw) +{ + ASSERT(i2c_dev); + + i2c_dev->hw = hw; + + return _i2c_m_sync_init_impl(&i2c_dev->service, hw); +} + +/** + * \brief Deinitialize sercom i2c module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_sync_deinit(struct _i2c_m_sync_device *const i2c_dev) +{ + ASSERT(i2c_dev); + + hri_sercomi2cm_clear_CTRLA_ENABLE_bit(i2c_dev->hw); + hri_sercomi2cm_set_CTRLA_SWRST_bit(i2c_dev->hw); + + return ERR_NONE; +} + +/** + * \brief Enable the i2c master module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_sync_enable(struct _i2c_m_sync_device *const i2c_dev) +{ + ASSERT(i2c_dev); + + _i2c_m_enable_implementation(i2c_dev->hw); + + return ERR_NONE; +} + +/** + * \brief Disable the i2c master module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_sync_disable(struct _i2c_m_sync_device *const i2c_dev) +{ + void *hw = i2c_dev->hw; + + ASSERT(i2c_dev); + ASSERT(i2c_dev->hw); + + hri_sercomi2cm_clear_CTRLA_ENABLE_bit(hw); + + return ERR_NONE; +} + +/** + * \brief Set baudrate of master + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] clkrate The clock rate of i2c master, in KHz + * \param[in] baudrate The baud rate desired for i2c master, in KHz + */ +int32_t _i2c_m_sync_set_baudrate(struct _i2c_m_sync_device *const i2c_dev, uint32_t clkrate, uint32_t baudrate) +{ + uint32_t tmp; + void * hw = i2c_dev->hw; + + if (hri_sercomi2cm_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + + tmp = _get_i2cm_index(hw); + clkrate = _i2cms[tmp].clk / 1000; + + if (i2c_dev->service.mode == I2C_STANDARD_MODE) { + tmp = (uint32_t)((clkrate - 10 * baudrate - baudrate * clkrate * (i2c_dev->service.trise * 0.000000001)) + / (2 * baudrate)); + hri_sercomi2cm_write_BAUD_BAUD_bf(hw, tmp); + } else if (i2c_dev->service.mode == I2C_FASTMODE) { + tmp = (uint32_t)((clkrate - 10 * baudrate - baudrate * clkrate * (i2c_dev->service.trise * 0.000000001)) + / (2 * baudrate)); + hri_sercomi2cm_write_BAUD_BAUD_bf(hw, tmp); + } else if (i2c_dev->service.mode == I2C_HIGHSPEED_MODE) { + tmp = (clkrate - 2 * baudrate) / (2 * baudrate); + hri_sercomi2cm_write_BAUD_HSBAUD_bf(hw, tmp); + } else { + /* error baudrate */ + return ERR_INVALID_ARG; + } + + return ERR_NONE; +} + +/** + * \brief Enable/disable I2C master interrupt + */ +void _i2c_m_async_set_irq_state(struct _i2c_m_async_device *const device, const enum _i2c_m_async_callback_type type, + const bool state) +{ + if (I2C_M_ASYNC_DEVICE_TX_COMPLETE == type || I2C_M_ASYNC_DEVICE_RX_COMPLETE == type) { + hri_sercomi2cm_write_INTEN_SB_bit(device->hw, state); + hri_sercomi2cm_write_INTEN_MB_bit(device->hw, state); + } else if (I2C_M_ASYNC_DEVICE_ERROR == type) { + hri_sercomi2cm_write_INTEN_ERROR_bit(device->hw, state); + } +} + +/** + * \brief Wait for bus response + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] flags Store the hardware response + * + * \return Bus response status. + * \retval 0 Bus response status OK + * \retval <0 Bus response fail + */ +inline static int32_t _sercom_i2c_sync_wait_bus(struct _i2c_m_sync_device *const i2c_dev, uint32_t *flags) +{ + uint32_t timeout = 65535; + void * hw = i2c_dev->hw; + + do { + *flags = hri_sercomi2cm_read_INTFLAG_reg(hw); + + if (timeout-- == 0) { + return I2C_ERR_BUS; + } + } while (!(*flags & MB_FLAG) && !(*flags & SB_FLAG)); + + return I2C_OK; +} + +/** + * \brief Send the slave address to bus, which will start the transfer + * + * \param[in] i2c_dev The pointer to i2c device + */ +static int32_t _sercom_i2c_sync_send_address(struct _i2c_m_sync_device *const i2c_dev) +{ + void * hw = i2c_dev->hw; + struct _i2c_m_msg *msg = &i2c_dev->service.msg; + int sclsm = hri_sercomi2cm_get_CTRLA_SCLSM_bit(hw); + uint32_t flags; + + ASSERT(i2c_dev); + + if (msg->len == 1 && sclsm) { + hri_sercomi2cm_set_CTRLB_ACKACT_bit(hw); + } else { + hri_sercomi2cm_clear_CTRLB_ACKACT_bit(hw); + } + + /* ten bit address */ + if (msg->addr & I2C_M_TEN) { + if (msg->flags & I2C_M_RD) { + msg->flags |= I2C_M_TEN; + } + + hri_sercomi2cm_write_ADDR_reg(hw, + ((msg->addr & TEN_ADDR_MASK) << 1) | SERCOM_I2CM_ADDR_TENBITEN + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + } else { + hri_sercomi2cm_write_ADDR_reg(hw, + ((msg->addr & SEVEN_ADDR_MASK) << 1) | (msg->flags & I2C_M_RD ? I2C_M_RD : 0x0) + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + } + + _sercom_i2c_sync_wait_bus(i2c_dev, &flags); + return _sercom_i2c_sync_analyse_flags(hw, flags, msg); +} + +/** + * \brief Transfer data specified by msg + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] msg The pointer to i2c message + * + * \return Transfer status. + * \retval 0 Transfer success + * \retval <0 Transfer fail or partial fail, return the error code + */ +int32_t _i2c_m_sync_transfer(struct _i2c_m_sync_device *const i2c_dev, struct _i2c_m_msg *msg) +{ + uint32_t flags; + int ret; + void * hw = i2c_dev->hw; + + ASSERT(i2c_dev); + ASSERT(i2c_dev->hw); + ASSERT(msg); + + if (i2c_dev->service.msg.flags & I2C_M_BUSY) { + return I2C_ERR_BUSY; + } + + msg->flags |= I2C_M_BUSY; + i2c_dev->service.msg = *msg; + + ret = _sercom_i2c_sync_send_address(i2c_dev); + + if (ret) { + i2c_dev->service.msg.flags &= ~I2C_M_BUSY; + + return ret; + } + + while (i2c_dev->service.msg.flags & I2C_M_BUSY) { + ret = _sercom_i2c_sync_wait_bus(i2c_dev, &flags); + + if (ret) { + if (msg->flags & I2C_M_STOP) { + _sercom_i2c_send_stop(hw); + } + + i2c_dev->service.msg.flags &= ~I2C_M_BUSY; + + return ret; + } + + ret = _sercom_i2c_sync_analyse_flags(hw, flags, &i2c_dev->service.msg); + } + + return ret; +} + +int32_t _i2c_m_sync_send_stop(struct _i2c_m_sync_device *const i2c_dev) +{ + void *hw = i2c_dev->hw; + + _sercom_i2c_send_stop(hw); + + return I2C_OK; +} + +static inline void _i2c_m_enable_implementation(void *const hw) +{ + int timeout = 65535; + + ASSERT(hw); + + /* Enable interrupts */ + hri_sercomi2cm_set_CTRLA_ENABLE_bit(hw); + + while (hri_sercomi2cm_read_STATUS_BUSSTATE_bf(hw) != I2C_IDLE) { + timeout--; + + if (timeout <= 0) { + hri_sercomi2cm_clear_STATUS_reg(hw, SERCOM_I2CM_STATUS_BUSSTATE(I2C_IDLE)); + } + } +} + +static int32_t _i2c_m_sync_init_impl(struct _i2c_m_service *const service, void *const hw) +{ + uint8_t i = _get_i2cm_index(hw); + + hri_sercomi2cm_wait_for_sync(hw, SERCOM_I2CM_SYNCBUSY_SWRST); + /* Check if module is enabled. */ + if (hri_sercomi2cm_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + hri_sercomi2cm_set_CTRLA_SWRST_bit(hw); + hri_sercomi2cm_wait_for_sync(hw, SERCOM_I2CM_SYNCBUSY_SWRST); + + hri_sercomi2cm_write_CTRLA_reg(hw, _i2cms[i].ctrl_a); + hri_sercomi2cm_write_CTRLB_reg(hw, _i2cms[i].ctrl_b); + hri_sercomi2cm_write_BAUD_reg(hw, _i2cms[i].baud); + + service->mode = (_i2cms[i].ctrl_a & SERCOM_I2CM_CTRLA_SPEED_Msk) >> SERCOM_I2CM_CTRLA_SPEED_Pos; + hri_sercomi2cm_write_ADDR_HS_bit(hw, service->mode < I2C_HS ? 0 : 1); + + service->trise = _i2cms[i].trise; + + return ERR_NONE; +} + +/* SERCOM I2C slave */ + +#ifndef CONF_SERCOM_0_I2CS_ENABLE +#define CONF_SERCOM_0_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_1_I2CS_ENABLE +#define CONF_SERCOM_1_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_2_I2CS_ENABLE +#define CONF_SERCOM_2_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_3_I2CS_ENABLE +#define CONF_SERCOM_3_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_4_I2CS_ENABLE +#define CONF_SERCOM_4_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_5_I2CS_ENABLE +#define CONF_SERCOM_5_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_6_I2CS_ENABLE +#define CONF_SERCOM_6_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_7_I2CS_ENABLE +#define CONF_SERCOM_7_I2CS_ENABLE 0 +#endif + +/** Amount of SERCOM that is used as I2C Slave. */ +#define SERCOM_I2CS_AMOUNT \ + (CONF_SERCOM_0_I2CS_ENABLE + CONF_SERCOM_1_I2CS_ENABLE + CONF_SERCOM_2_I2CS_ENABLE + CONF_SERCOM_3_I2CS_ENABLE \ + + CONF_SERCOM_4_I2CS_ENABLE \ + + CONF_SERCOM_5_I2CS_ENABLE \ + + CONF_SERCOM_6_I2CS_ENABLE \ + + CONF_SERCOM_7_I2CS_ENABLE) + +/** + * \brief Macro is used to fill I2C slave configuration structure based on + * its number + * + * \param[in] n The number of structures + */ +#define I2CS_CONFIGURATION(n) \ + { \ + n, SERCOM_I2CM_CTRLA_MODE_I2C_SLAVE | (CONF_SERCOM_##n##_I2CS_RUNSTDBY << SERCOM_I2CS_CTRLA_RUNSTDBY_Pos) \ + | SERCOM_I2CS_CTRLA_SDAHOLD(CONF_SERCOM_##n##_I2CS_SDAHOLD) \ + | (CONF_SERCOM_##n##_I2CS_SEXTTOEN << SERCOM_I2CS_CTRLA_SEXTTOEN_Pos) \ + | (CONF_SERCOM_##n##_I2CS_SPEED << SERCOM_I2CS_CTRLA_SPEED_Pos) \ + | (CONF_SERCOM_##n##_I2CS_SCLSM << SERCOM_I2CS_CTRLA_SCLSM_Pos) \ + | (CONF_SERCOM_##n##_I2CS_LOWTOUT << SERCOM_I2CS_CTRLA_LOWTOUTEN_Pos), \ + SERCOM_I2CS_CTRLB_SMEN | SERCOM_I2CS_CTRLB_AACKEN | SERCOM_I2CS_CTRLB_AMODE(CONF_SERCOM_##n##_I2CS_AMODE), \ + (CONF_SERCOM_##n##_I2CS_GENCEN << SERCOM_I2CS_ADDR_GENCEN_Pos) \ + | SERCOM_I2CS_ADDR_ADDR(CONF_SERCOM_##n##_I2CS_ADDRESS) \ + | (CONF_SERCOM_##n##_I2CS_TENBITEN << SERCOM_I2CS_ADDR_TENBITEN_Pos) \ + | SERCOM_I2CS_ADDR_ADDRMASK(CONF_SERCOM_##n##_I2CS_ADDRESS_MASK) \ + } + +/** + * \brief Macro to check 10-bit addressing + */ +#define I2CS_7BIT_ADDRESSING_MASK 0x7F + +static int32_t _i2c_s_init(void *const hw); +static int8_t _get_i2c_s_index(const void *const hw); +static inline void _i2c_s_deinit(void *const hw); +static int32_t _i2c_s_set_address(void *const hw, const uint16_t address); + +/** + * \brief SERCOM I2C slave configuration type + */ +struct i2cs_configuration { + uint8_t number; + hri_sercomi2cs_ctrla_reg_t ctrl_a; + hri_sercomi2cs_ctrlb_reg_t ctrl_b; + hri_sercomi2cs_addr_reg_t address; +}; + +#if SERCOM_I2CS_AMOUNT < 1 +/** Dummy array for compiling. */ +static struct i2cs_configuration _i2css[1] = {{0}}; +#else +/** + * \brief Array of SERCOM I2C slave configurations + */ +static struct i2cs_configuration _i2css[] = { +#if CONF_SERCOM_0_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(0), +#endif +#if CONF_SERCOM_1_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(1), +#endif +#if CONF_SERCOM_2_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(2), +#endif +#if CONF_SERCOM_3_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(3), +#endif +#if CONF_SERCOM_4_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(4), +#endif +#if CONF_SERCOM_5_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(5), +#endif +}; +#endif + +/** + * \brief Initialize synchronous I2C slave + */ +int32_t _i2c_s_sync_init(struct _i2c_s_sync_device *const device, void *const hw) +{ + int32_t status; + + ASSERT(device); + + status = _i2c_s_init(hw); + if (status) { + return status; + } + device->hw = hw; + + return ERR_NONE; +} + +/** + * \brief Initialize asynchronous I2C slave + */ +int32_t _i2c_s_async_init(struct _i2c_s_async_device *const device, void *const hw) +{ + int32_t init_status; + + ASSERT(device); + + init_status = _i2c_s_init(hw); + if (init_status) { + return init_status; + } + + device->hw = hw; + _sercom_init_irq_param(hw, (void *)device); + uint8_t irq = _sercom_get_irq_num(hw); + for (uint32_t i = 0; i < 4; i++) { + NVIC_DisableIRQ((IRQn_Type)irq); + NVIC_ClearPendingIRQ((IRQn_Type)irq); + NVIC_EnableIRQ((IRQn_Type)irq); + irq++; + } + + return ERR_NONE; +} + +/** + * \brief Deinitialize synchronous I2C + */ +int32_t _i2c_s_sync_deinit(struct _i2c_s_sync_device *const device) +{ + _i2c_s_deinit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Deinitialize asynchronous I2C + */ +int32_t _i2c_s_async_deinit(struct _i2c_s_async_device *const device) +{ + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(device->hw)); + _i2c_s_deinit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Enable I2C module + */ +int32_t _i2c_s_sync_enable(struct _i2c_s_sync_device *const device) +{ + hri_sercomi2cs_set_CTRLA_ENABLE_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Enable I2C module + */ +int32_t _i2c_s_async_enable(struct _i2c_s_async_device *const device) +{ + hri_sercomi2cs_set_CTRLA_ENABLE_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Disable I2C module + */ +int32_t _i2c_s_sync_disable(struct _i2c_s_sync_device *const device) +{ + hri_sercomi2cs_clear_CTRLA_ENABLE_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Disable I2C module + */ +int32_t _i2c_s_async_disable(struct _i2c_s_async_device *const device) +{ + hri_sercomi2cs_clear_CTRLA_ENABLE_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Check if 10-bit addressing mode is on + */ +int32_t _i2c_s_sync_is_10bit_addressing_on(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_get_ADDR_TENBITEN_bit(device->hw); +} + +/** + * \brief Check if 10-bit addressing mode is on + */ +int32_t _i2c_s_async_is_10bit_addressing_on(const struct _i2c_s_async_device *const device) +{ + return hri_sercomi2cs_get_ADDR_TENBITEN_bit(device->hw); +} + +/** + * \brief Set I2C slave address + */ +int32_t _i2c_s_sync_set_address(struct _i2c_s_sync_device *const device, const uint16_t address) +{ + return _i2c_s_set_address(device->hw, address); +} + +/** + * \brief Set I2C slave address + */ +int32_t _i2c_s_async_set_address(struct _i2c_s_async_device *const device, const uint16_t address) +{ + return _i2c_s_set_address(device->hw, address); +} + +/** + * \brief Write a byte to the given I2C instance + */ +void _i2c_s_sync_write_byte(struct _i2c_s_sync_device *const device, const uint8_t data) +{ + hri_sercomi2cs_write_DATA_reg(device->hw, data); +} + +/** + * \brief Write a byte to the given I2C instance + */ +void _i2c_s_async_write_byte(struct _i2c_s_async_device *const device, const uint8_t data) +{ + hri_sercomi2cs_write_DATA_reg(device->hw, data); +} + +/** + * \brief Read a byte from the given I2C instance + */ +uint8_t _i2c_s_sync_read_byte(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_read_DATA_reg(device->hw); +} + +/** + * \brief Check if I2C is ready to send next byt + */ +bool _i2c_s_sync_is_byte_sent(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_get_interrupt_DRDY_bit(device->hw); +} + +/** + * \brief Check if there is data received by I2C + */ +bool _i2c_s_sync_is_byte_received(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_get_interrupt_DRDY_bit(device->hw); +} + +/** + * \brief Retrieve I2C slave status + */ +i2c_s_status_t _i2c_s_sync_get_status(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_read_STATUS_reg(device->hw); +} + +/** + * \brief Retrieve I2C slave status + */ +i2c_s_status_t _i2c_s_async_get_status(const struct _i2c_s_async_device *const device) +{ + return hri_sercomi2cs_read_STATUS_reg(device->hw); +} + +/** + * \brief Abort data transmission + */ +int32_t _i2c_s_async_abort_transmission(const struct _i2c_s_async_device *const device) +{ + hri_sercomi2cs_clear_INTEN_DRDY_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Enable/disable I2C slave interrupt + */ +int32_t _i2c_s_async_set_irq_state(struct _i2c_s_async_device *const device, const enum _i2c_s_async_callback_type type, + const bool state) +{ + ASSERT(device); + + if (I2C_S_DEVICE_TX == type || I2C_S_DEVICE_RX_COMPLETE == type) { + hri_sercomi2cs_write_INTEN_DRDY_bit(device->hw, state); + } else if (I2C_S_DEVICE_ERROR == type) { + hri_sercomi2cs_write_INTEN_ERROR_bit(device->hw, state); + } + + return ERR_NONE; +} + +/** + * \internal Initalize i2c slave hardware + * + * \param[in] p The pointer to hardware instance + * + *\ return status of initialization + */ +static int32_t _i2c_s_init(void *const hw) +{ + int8_t i = _get_i2c_s_index(hw); + if (i == -1) { + return ERR_INVALID_ARG; + } + + hri_sercomi2cs_wait_for_sync(hw, SERCOM_I2CS_CTRLA_SWRST); + if (hri_sercomi2cs_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + hri_sercomi2cs_set_CTRLA_SWRST_bit(hw); + hri_sercomi2cs_wait_for_sync(hw, SERCOM_I2CS_CTRLA_SWRST); + + hri_sercomi2cs_write_CTRLA_reg(hw, _i2css[i].ctrl_a); + hri_sercomi2cs_write_CTRLB_reg(hw, _i2css[i].ctrl_b); + hri_sercomi2cs_write_ADDR_reg(hw, _i2css[i].address); + + return ERR_NONE; +} + +/** + * \internal Retrieve ordinal number of the given sercom hardware instance + * + * \param[in] hw The pointer to hardware instance + * + * \return The ordinal number of the given sercom hardware instance + */ +static int8_t _get_i2c_s_index(const void *const hw) +{ + uint8_t sercom_offset = _sercom_get_hardware_index(hw); + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_i2css); i++) { + if (_i2css[i].number == sercom_offset) { + return i; + } + } + + ASSERT(false); + return -1; +} + +/** + * \internal De-initialize i2c slave + * + * \param[in] hw The pointer to hardware instance + */ +static inline void _i2c_s_deinit(void *const hw) +{ + hri_sercomi2cs_clear_CTRLA_ENABLE_bit(hw); + hri_sercomi2cs_set_CTRLA_SWRST_bit(hw); +} + +/** + * \internal De-initialize i2c slave + * + * \param[in] hw The pointer to hardware instance + * \param[in] address Address to set + */ +static int32_t _i2c_s_set_address(void *const hw, const uint16_t address) +{ + bool enabled; + + enabled = hri_sercomi2cs_get_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomi2cs_clear_CTRLA_ENABLE_bit(hw); + hri_sercomi2cs_write_ADDR_ADDR_bf(hw, address); + CRITICAL_SECTION_LEAVE() + + if (enabled) { + hri_sercomi2cs_set_CTRLA_ENABLE_bit(hw); + } + + return ERR_NONE; +} + +/* Sercom SPI implementation */ + +#ifndef SERCOM_USART_CTRLA_MODE_SPI_SLAVE +#define SERCOM_USART_CTRLA_MODE_SPI_SLAVE (2 << 2) +#endif + +#define SPI_DEV_IRQ_MODE 0x8000 + +#define _SPI_CS_PORT_EXTRACT(cs) (((cs) >> 0) & 0xFF) +#define _SPI_CS_PIN_EXTRACT(cs) (((cs) >> 8) & 0xFF) + +COMPILER_PACK_SET(1) +/** Initialization configuration of registers. */ +struct sercomspi_regs_cfg { + uint32_t ctrla; + uint32_t ctrlb; + uint32_t addr; + uint8_t baud; + uint8_t dbgctrl; + uint16_t dummy_byte; + uint8_t n; +}; +COMPILER_PACK_RESET() + +/** Build configuration from header macros. */ +#define SERCOMSPI_REGS(n) \ + { \ + ((CONF_SERCOM_##n##_SPI_DORD) | (CONF_SERCOM_##n##_SPI_CPOL << SERCOM_SPI_CTRLA_CPOL_Pos) \ + | (CONF_SERCOM_##n##_SPI_CPHA << SERCOM_SPI_CTRLA_CPHA_Pos) \ + | (CONF_SERCOM_##n##_SPI_AMODE_EN ? SERCOM_SPI_CTRLA_FORM(2) : SERCOM_SPI_CTRLA_FORM(0)) \ + | SERCOM_SPI_CTRLA_DOPO(CONF_SERCOM_##n##_SPI_TXPO) \ + | SERCOM_SPI_CTRLA_DIPO(CONF_SERCOM_##n##_SPI_RXPO) \ + | (CONF_SERCOM_##n##_SPI_IBON << SERCOM_SPI_CTRLA_IBON_Pos) \ + | (CONF_SERCOM_##n##_SPI_RUNSTDBY << SERCOM_SPI_CTRLA_RUNSTDBY_Pos) \ + | SERCOM_SPI_CTRLA_MODE(CONF_SERCOM_##n##_SPI_MODE)), /* ctrla */ \ + ((CONF_SERCOM_##n##_SPI_RXEN << SERCOM_SPI_CTRLB_RXEN_Pos) \ + | (CONF_SERCOM_##n##_SPI_MSSEN << SERCOM_SPI_CTRLB_MSSEN_Pos) \ + | (CONF_SERCOM_##n##_SPI_SSDE << SERCOM_SPI_CTRLB_SSDE_Pos) \ + | (CONF_SERCOM_##n##_SPI_PLOADEN << SERCOM_SPI_CTRLB_PLOADEN_Pos) \ + | SERCOM_SPI_CTRLB_AMODE(CONF_SERCOM_##n##_SPI_AMODE) \ + | SERCOM_SPI_CTRLB_CHSIZE(CONF_SERCOM_##n##_SPI_CHSIZE)), /* ctrlb */ \ + (SERCOM_SPI_ADDR_ADDR(CONF_SERCOM_##n##_SPI_ADDR) \ + | SERCOM_SPI_ADDR_ADDRMASK(CONF_SERCOM_##n##_SPI_ADDRMASK)), /* addr */ \ + ((uint8_t)CONF_SERCOM_##n##_SPI_BAUD_RATE), /* baud */ \ + (CONF_SERCOM_##n##_SPI_DBGSTOP << SERCOM_SPI_DBGCTRL_DBGSTOP_Pos), /* dbgctrl */ \ + CONF_SERCOM_##n##_SPI_DUMMYBYTE, /* Dummy byte for SPI master mode */ \ + n /* sercom number */ \ + } + +#ifndef CONF_SERCOM_0_SPI_ENABLE +#define CONF_SERCOM_0_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_1_SPI_ENABLE +#define CONF_SERCOM_1_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_2_SPI_ENABLE +#define CONF_SERCOM_2_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_3_SPI_ENABLE +#define CONF_SERCOM_3_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_4_SPI_ENABLE +#define CONF_SERCOM_4_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_5_SPI_ENABLE +#define CONF_SERCOM_5_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_6_SPI_ENABLE +#define CONF_SERCOM_6_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_7_SPI_ENABLE +#define CONF_SERCOM_7_SPI_ENABLE 0 +#endif + +/** Amount of SERCOM that is used as SPI */ +#define SERCOM_SPI_AMOUNT \ + (CONF_SERCOM_0_SPI_ENABLE + CONF_SERCOM_1_SPI_ENABLE + CONF_SERCOM_2_SPI_ENABLE + CONF_SERCOM_3_SPI_ENABLE \ + + CONF_SERCOM_4_SPI_ENABLE \ + + CONF_SERCOM_5_SPI_ENABLE \ + + CONF_SERCOM_6_SPI_ENABLE \ + + CONF_SERCOM_7_SPI_ENABLE) + +#if SERCOM_SPI_AMOUNT < 1 +/** Dummy array for compiling. */ +static const struct sercomspi_regs_cfg sercomspi_regs[1] = {{0}}; +#else +/** The SERCOM SPI configurations of SERCOM that is used as SPI. */ +static const struct sercomspi_regs_cfg sercomspi_regs[] = { +#if CONF_SERCOM_0_SPI_ENABLE + SERCOMSPI_REGS(0), +#endif +#if CONF_SERCOM_1_SPI_ENABLE + SERCOMSPI_REGS(1), +#endif +#if CONF_SERCOM_2_SPI_ENABLE + SERCOMSPI_REGS(2), +#endif +#if CONF_SERCOM_3_SPI_ENABLE + SERCOMSPI_REGS(3), +#endif +#if CONF_SERCOM_4_SPI_ENABLE + SERCOMSPI_REGS(4), +#endif +#if CONF_SERCOM_5_SPI_ENABLE + SERCOMSPI_REGS(5), +#endif +#if CONF_SERCOM_6_SPI_ENABLE + SERCOMSPI_REGS(6), +#endif +#if CONF_SERCOM_7_SPI_ENABLE + SERCOMSPI_REGS(7), +#endif +}; +#endif + +/** \internal De-initialize SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return De-initialization status + */ +static int32_t _spi_deinit(void *const hw) +{ + hri_sercomspi_clear_CTRLA_ENABLE_bit(hw); + hri_sercomspi_set_CTRLA_SWRST_bit(hw); + + return ERR_NONE; +} + +/** \internal Enable SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return Enabling status + */ +static int32_t _spi_sync_enable(void *const hw) +{ + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST)) { + return ERR_BUSY; + } + + hri_sercomspi_set_CTRLA_ENABLE_bit(hw); + + return ERR_NONE; +} + +/** \internal Enable SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return Enabling status + */ +static int32_t _spi_async_enable(void *const hw) +{ + _spi_sync_enable(hw); + NVIC_EnableIRQ(_sercom_get_irq_num(hw)); + uint8_t irq = _sercom_get_irq_num(hw); + for (uint32_t i = 0; i < 4; i++) { + NVIC_EnableIRQ(irq++); + } + + return ERR_NONE; +} + +/** \internal Disable SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return Disabling status + */ +static int32_t _spi_sync_disable(void *const hw) +{ + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST)) { + return ERR_BUSY; + } + hri_sercomspi_clear_CTRLA_ENABLE_bit(hw); + + return ERR_NONE; +} + +/** \internal Disable SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return Disabling status + */ +static int32_t _spi_async_disable(void *const hw) +{ + _spi_sync_disable(hw); + hri_sercomspi_clear_INTEN_reg( + hw, SERCOM_SPI_INTFLAG_ERROR | SERCOM_SPI_INTFLAG_RXC | SERCOM_SPI_INTFLAG_TXC | SERCOM_SPI_INTFLAG_DRE); + uint8_t irq = _sercom_get_irq_num(hw); + for (uint32_t i = 0; i < 4; i++) { + NVIC_DisableIRQ(irq++); + } + + return ERR_NONE; +} + +/** \internal Set SERCOM SPI mode + * + * \param[in] hw Pointer to the hardware register base. + * \param[in] mode The mode to set + * + * \return Setting mode status + */ +static int32_t _spi_set_mode(void *const hw, const enum spi_transfer_mode mode) +{ + uint32_t ctrla; + + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST | SERCOM_SPI_SYNCBUSY_ENABLE)) { + return ERR_BUSY; + } + + ctrla = hri_sercomspi_read_CTRLA_reg(hw); + ctrla &= ~(SERCOM_SPI_CTRLA_CPOL | SERCOM_SPI_CTRLA_CPHA); + ctrla |= (mode & 0x3u) << SERCOM_SPI_CTRLA_CPHA_Pos; + hri_sercomspi_write_CTRLA_reg(hw, ctrla); + + return ERR_NONE; +} + +/** \internal Set SERCOM SPI baudrate + * + * \param[in] hw Pointer to the hardware register base. + * \param[in] baud_val The baudrate to set + * + * \return Setting baudrate status + */ +static int32_t _spi_set_baudrate(void *const hw, const uint32_t baud_val) +{ + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST)) { + return ERR_BUSY; + } + + hri_sercomspi_write_BAUD_reg(hw, baud_val); + + return ERR_NONE; +} + +/** \internal Set SERCOM SPI char size + * + * \param[in] hw Pointer to the hardware register base. + * \param[in] baud_val The baudrate to set + * \param[out] size Stored char size + * + * \return Setting char size status + */ +static int32_t _spi_set_char_size(void *const hw, const enum spi_char_size char_size, uint8_t *const size) +{ + /* Only 8-bit or 9-bit accepted */ + if (!(char_size == SPI_CHAR_SIZE_8 || char_size == SPI_CHAR_SIZE_9)) { + return ERR_INVALID_ARG; + } + + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST | SERCOM_SPI_SYNCBUSY_CTRLB)) { + return ERR_BUSY; + } + + hri_sercomspi_write_CTRLB_CHSIZE_bf(hw, char_size); + *size = (char_size == SPI_CHAR_SIZE_8) ? 1 : 2; + + return ERR_NONE; +} + +/** \internal Set SERCOM SPI data order + * + * \param[in] hw Pointer to the hardware register base. + * \param[in] baud_val The baudrate to set + * + * \return Setting data order status + */ +static int32_t _spi_set_data_order(void *const hw, const enum spi_data_order dord) +{ + uint32_t ctrla; + + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST)) { + return ERR_BUSY; + } + + ctrla = hri_sercomspi_read_CTRLA_reg(hw); + + if (dord == SPI_DATA_ORDER_LSB_1ST) { + ctrla |= SERCOM_SPI_CTRLA_DORD; + } else { + ctrla &= ~SERCOM_SPI_CTRLA_DORD; + } + hri_sercomspi_write_CTRLA_reg(hw, ctrla); + + return ERR_NONE; +} + +/** \brief Load SERCOM registers to init for SPI master mode + * The settings will be applied with default master mode, unsupported things + * are ignored. + * \param[in, out] hw Pointer to the hardware register base. + * \param[in] regs Pointer to register configuration values. + */ +static inline void _spi_load_regs_master(void *const hw, const struct sercomspi_regs_cfg *regs) +{ + ASSERT(hw && regs); + hri_sercomspi_write_CTRLA_reg( + hw, regs->ctrla & ~(SERCOM_SPI_CTRLA_IBON | SERCOM_SPI_CTRLA_ENABLE | SERCOM_SPI_CTRLA_SWRST)); + hri_sercomspi_write_CTRLB_reg( + hw, + (regs->ctrlb + & ~(SERCOM_SPI_CTRLB_MSSEN | SERCOM_SPI_CTRLB_AMODE_Msk | SERCOM_SPI_CTRLB_SSDE | SERCOM_SPI_CTRLB_PLOADEN)) + | (SERCOM_SPI_CTRLB_RXEN)); + hri_sercomspi_write_BAUD_reg(hw, regs->baud); + hri_sercomspi_write_DBGCTRL_reg(hw, regs->dbgctrl); +} + +/** \brief Load SERCOM registers to init for SPI slave mode + * The settings will be applied with default slave mode, unsupported things + * are ignored. + * \param[in, out] hw Pointer to the hardware register base. + * \param[in] regs Pointer to register configuration values. + */ +static inline void _spi_load_regs_slave(void *const hw, const struct sercomspi_regs_cfg *regs) +{ + ASSERT(hw && regs); + hri_sercomspi_write_CTRLA_reg( + hw, regs->ctrla & ~(SERCOM_SPI_CTRLA_IBON | SERCOM_SPI_CTRLA_ENABLE | SERCOM_SPI_CTRLA_SWRST)); + hri_sercomspi_write_CTRLB_reg(hw, + (regs->ctrlb & ~(SERCOM_SPI_CTRLB_MSSEN)) + | (SERCOM_SPI_CTRLB_RXEN | SERCOM_SPI_CTRLB_SSDE | SERCOM_SPI_CTRLB_PLOADEN)); + hri_sercomspi_write_ADDR_reg(hw, regs->addr); + hri_sercomspi_write_DBGCTRL_reg(hw, regs->dbgctrl); + while (hri_sercomspi_is_syncing(hw, 0xFFFFFFFF)) + ; +} + +/** \brief Return the pointer to register settings of specific SERCOM + * \param[in] hw_addr The hardware register base address. + * \return Pointer to register settings of specific SERCOM. + */ +static inline const struct sercomspi_regs_cfg *_spi_get_regs(const uint32_t hw_addr) +{ + uint8_t n = _sercom_get_hardware_index((const void *)hw_addr); + uint8_t i; + + for (i = 0; i < sizeof(sercomspi_regs) / sizeof(struct sercomspi_regs_cfg); i++) { + if (sercomspi_regs[i].n == n) { + return &sercomspi_regs[i]; + } + } + + return NULL; +} + +int32_t _spi_m_sync_init(struct _spi_m_sync_dev *dev, void *const hw) +{ + const struct sercomspi_regs_cfg *regs = _spi_get_regs((uint32_t)hw); + + ASSERT(dev && hw); + + if (regs == NULL) { + return ERR_INVALID_ARG; + } + + hri_sercomspi_wait_for_sync(hw, SERCOM_SPI_SYNCBUSY_SWRST); + if (hri_sercomspi_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + hri_sercomspi_set_CTRLA_SWRST_bit(hw); + hri_sercomspi_wait_for_sync(hw, SERCOM_SPI_SYNCBUSY_SWRST); + dev->prvt = hw; + + if ((regs->ctrla & SERCOM_SPI_CTRLA_MODE_Msk) == SERCOM_USART_CTRLA_MODE_SPI_SLAVE) { + _spi_load_regs_slave(hw, regs); + } else { + _spi_load_regs_master(hw, regs); + } + + /* Load character size from default hardware configuration */ + dev->char_size = ((regs->ctrlb & SERCOM_SPI_CTRLB_CHSIZE_Msk) == 0) ? 1 : 2; + + dev->dummy_byte = regs->dummy_byte; + + return ERR_NONE; +} + +int32_t _spi_s_sync_init(struct _spi_s_sync_dev *dev, void *const hw) +{ + return _spi_m_sync_init(dev, hw); +} + +int32_t _spi_m_async_init(struct _spi_async_dev *dev, void *const hw) +{ + struct _spi_async_dev *spid = dev; + /* Do hardware initialize. */ + int32_t rc = _spi_m_sync_init((struct _spi_m_sync_dev *)dev, hw); + + if (rc < 0) { + return rc; + } + + _sercom_init_irq_param(hw, (void *)dev); + /* Initialize callbacks: must use them */ + spid->callbacks.complete = NULL; + spid->callbacks.rx = NULL; + spid->callbacks.tx = NULL; + uint8_t irq = _sercom_get_irq_num(hw); + for (uint32_t i = 0; i < 4; i++) { + NVIC_DisableIRQ((IRQn_Type)irq); + NVIC_ClearPendingIRQ((IRQn_Type)irq); + irq++; + } + + return ERR_NONE; +} + +int32_t _spi_s_async_init(struct _spi_s_async_dev *dev, void *const hw) +{ + return _spi_m_async_init(dev, hw); +} + +int32_t _spi_m_async_deinit(struct _spi_async_dev *dev) +{ + NVIC_DisableIRQ(_sercom_get_irq_num(dev->prvt)); + NVIC_ClearPendingIRQ(_sercom_get_irq_num(dev->prvt)); + + return _spi_deinit(dev->prvt); +} + +int32_t _spi_s_async_deinit(struct _spi_s_async_dev *dev) +{ + NVIC_DisableIRQ(_sercom_get_irq_num(dev->prvt)); + NVIC_ClearPendingIRQ(_sercom_get_irq_num(dev->prvt)); + + return _spi_deinit(dev->prvt); +} + +int32_t _spi_m_sync_deinit(struct _spi_m_sync_dev *dev) +{ + return _spi_deinit(dev->prvt); +} + +int32_t _spi_s_sync_deinit(struct _spi_s_sync_dev *dev) +{ + return _spi_deinit(dev->prvt); +} + +int32_t _spi_m_sync_enable(struct _spi_m_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_sync_enable(dev->prvt); +} + +int32_t _spi_s_sync_enable(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_sync_enable(dev->prvt); +} + +int32_t _spi_m_async_enable(struct _spi_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_async_enable(dev->prvt); +} + +int32_t _spi_s_async_enable(struct _spi_s_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_async_enable(dev->prvt); +} + +int32_t _spi_m_sync_disable(struct _spi_m_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_sync_disable(dev->prvt); +} + +int32_t _spi_s_sync_disable(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_sync_disable(dev->prvt); +} + +int32_t _spi_m_async_disable(struct _spi_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_async_disable(dev->prvt); +} + +int32_t _spi_s_async_disable(struct _spi_s_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_async_disable(dev->prvt); +} + +int32_t _spi_m_sync_set_mode(struct _spi_m_sync_dev *dev, const enum spi_transfer_mode mode) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_mode(dev->prvt, mode); +} + +int32_t _spi_m_async_set_mode(struct _spi_async_dev *dev, const enum spi_transfer_mode mode) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_mode(dev->prvt, mode); +} + +int32_t _spi_s_async_set_mode(struct _spi_s_async_dev *dev, const enum spi_transfer_mode mode) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_mode(dev->prvt, mode); +} + +int32_t _spi_s_sync_set_mode(struct _spi_s_sync_dev *dev, const enum spi_transfer_mode mode) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_mode(dev->prvt, mode); +} + +int32_t _spi_calc_baud_val(struct spi_dev *dev, const uint32_t clk, const uint32_t baud) +{ + int32_t rc; + ASSERT(dev); + + /* Not accept 0es */ + if (clk == 0 || baud == 0) { + return ERR_INVALID_ARG; + } + + /* Check baudrate range of current assigned clock */ + if (!(baud <= (clk >> 1) && baud >= (clk >> 8))) { + return ERR_INVALID_ARG; + } + + rc = ((clk >> 1) / baud) - 1; + return rc; +} + +int32_t _spi_m_sync_set_baudrate(struct _spi_m_sync_dev *dev, const uint32_t baud_val) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_baudrate(dev->prvt, baud_val); +} + +int32_t _spi_m_async_set_baudrate(struct _spi_async_dev *dev, const uint32_t baud_val) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_baudrate(dev->prvt, baud_val); +} + +int32_t _spi_m_sync_set_char_size(struct _spi_m_sync_dev *dev, const enum spi_char_size char_size) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_char_size(dev->prvt, char_size, &dev->char_size); +} + +int32_t _spi_m_async_set_char_size(struct _spi_async_dev *dev, const enum spi_char_size char_size) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_char_size(dev->prvt, char_size, &dev->char_size); +} + +int32_t _spi_s_async_set_char_size(struct _spi_s_async_dev *dev, const enum spi_char_size char_size) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_char_size(dev->prvt, char_size, &dev->char_size); +} + +int32_t _spi_s_sync_set_char_size(struct _spi_s_sync_dev *dev, const enum spi_char_size char_size) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_char_size(dev->prvt, char_size, &dev->char_size); +} + +int32_t _spi_m_sync_set_data_order(struct _spi_m_sync_dev *dev, const enum spi_data_order dord) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_data_order(dev->prvt, dord); +} + +int32_t _spi_m_async_set_data_order(struct _spi_async_dev *dev, const enum spi_data_order dord) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_data_order(dev->prvt, dord); +} + +int32_t _spi_s_async_set_data_order(struct _spi_s_async_dev *dev, const enum spi_data_order dord) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_data_order(dev->prvt, dord); +} + +int32_t _spi_s_sync_set_data_order(struct _spi_s_sync_dev *dev, const enum spi_data_order dord) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_data_order(dev->prvt, dord); +} + +/** Wait until SPI bus idle. */ +static inline void _spi_wait_bus_idle(void *const hw) +{ + while (!(hri_sercomspi_get_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_TXC | SERCOM_SPI_INTFLAG_DRE))) { + ; + } + hri_sercomspi_clear_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_TXC | SERCOM_SPI_INTFLAG_DRE); +} + +/** Holds run time information for message sync transaction. */ +struct _spi_trans_ctrl { + /** Pointer to transmitting data buffer. */ + uint8_t *txbuf; + /** Pointer to receiving data buffer. */ + uint8_t *rxbuf; + /** Count number of data transmitted. */ + uint32_t txcnt; + /** Count number of data received. */ + uint32_t rxcnt; + /** Data character size. */ + uint8_t char_size; +}; + +/** Check interrupt flag of RXC and update transaction runtime information. */ +static inline bool _spi_rx_check_and_receive(void *const hw, const uint32_t iflag, struct _spi_trans_ctrl *ctrl) +{ + uint32_t data; + + if (!(iflag & SERCOM_SPI_INTFLAG_RXC)) { + return false; + } + + data = hri_sercomspi_read_DATA_reg(hw); + + if (ctrl->rxbuf) { + *ctrl->rxbuf++ = (uint8_t)data; + + if (ctrl->char_size > 1) { + *ctrl->rxbuf++ = (uint8_t)(data >> 8); + } + } + + ctrl->rxcnt++; + + return true; +} + +/** Check interrupt flag of DRE and update transaction runtime information. */ +static inline void _spi_tx_check_and_send(void *const hw, const uint32_t iflag, struct _spi_trans_ctrl *ctrl, + uint16_t dummy) +{ + uint32_t data; + + if (!(SERCOM_SPI_INTFLAG_DRE & iflag)) { + return; + } + + if (ctrl->txbuf) { + data = *ctrl->txbuf++; + + if (ctrl->char_size > 1) { + data |= (*ctrl->txbuf) << 8; + ctrl->txbuf++; + } + } else { + data = dummy; + } + + ctrl->txcnt++; + hri_sercomspi_write_DATA_reg(hw, data); +} + +/** Check interrupt flag of ERROR and update transaction runtime information. */ +static inline int32_t _spi_err_check(const uint32_t iflag, void *const hw) +{ + if (SERCOM_SPI_INTFLAG_ERROR & iflag) { + hri_sercomspi_clear_STATUS_reg(hw, ~0); + hri_sercomspi_clear_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_ERROR); + return ERR_OVERFLOW; + } + + return ERR_NONE; +} + +int32_t _spi_m_sync_trans(struct _spi_m_sync_dev *dev, const struct spi_msg *msg) +{ + void * hw = dev->prvt; + int32_t rc = 0; + struct _spi_trans_ctrl ctrl = {msg->txbuf, msg->rxbuf, 0, 0, dev->char_size}; + + ASSERT(dev && hw); + + /* If settings are not applied (pending), we can not go on */ + if (hri_sercomspi_is_syncing( + hw, (SERCOM_SPI_SYNCBUSY_SWRST | SERCOM_SPI_SYNCBUSY_ENABLE | SERCOM_SPI_SYNCBUSY_CTRLB))) { + return ERR_BUSY; + } + + /* SPI must be enabled to start synchronous transfer */ + if (!hri_sercomspi_get_CTRLA_ENABLE_bit(hw)) { + return ERR_NOT_INITIALIZED; + } + + for (;;) { + uint32_t iflag = hri_sercomspi_read_INTFLAG_reg(hw); + + if (!_spi_rx_check_and_receive(hw, iflag, &ctrl)) { + /* In master mode, do not start next byte before previous byte received + * to make better output waveform */ + if (ctrl.rxcnt >= ctrl.txcnt) { + _spi_tx_check_and_send(hw, iflag, &ctrl, dev->dummy_byte); + } + } + + rc = _spi_err_check(iflag, hw); + + if (rc < 0) { + break; + } + if (ctrl.txcnt >= msg->size && ctrl.rxcnt >= msg->size) { + rc = ctrl.txcnt; + break; + } + } + /* Wait until SPI bus idle */ + _spi_wait_bus_idle(hw); + + return rc; +} + +int32_t _spi_m_async_enable_tx(struct _spi_async_dev *dev, bool state) +{ + void *hw = dev->prvt; + + ASSERT(dev && hw); + + if (state) { + hri_sercomspi_set_INTEN_DRE_bit(hw); + } else { + hri_sercomspi_clear_INTEN_DRE_bit(hw); + } + + return ERR_NONE; +} + +int32_t _spi_s_async_enable_tx(struct _spi_s_async_dev *dev, bool state) +{ + return _spi_m_async_enable_tx(dev, state); +} + +int32_t _spi_m_async_enable_rx(struct _spi_async_dev *dev, bool state) +{ + void *hw = dev->prvt; + + ASSERT(dev); + ASSERT(hw); + + if (state) { + hri_sercomspi_set_INTEN_RXC_bit(hw); + } else { + hri_sercomspi_clear_INTEN_RXC_bit(hw); + } + + return ERR_NONE; +} + +int32_t _spi_s_async_enable_rx(struct _spi_s_async_dev *dev, bool state) +{ + return _spi_m_async_enable_rx(dev, state); +} + +int32_t _spi_m_async_enable_ss_detect(struct _spi_async_dev *dev, bool state) +{ + ASSERT(dev && dev->prvt); + + if (state) { + hri_sercomspi_set_INTEN_TXC_bit(dev->prvt); + } else { + hri_sercomspi_clear_INTEN_TXC_bit(dev->prvt); + } + + return ERR_NONE; +} + +int32_t _spi_s_async_enable_ss_detect(struct _spi_s_async_dev *dev, bool state) +{ + return _spi_m_async_enable_ss_detect(dev, state); +} + +int32_t _spi_m_async_write_one(struct _spi_async_dev *dev, uint16_t data) +{ + ASSERT(dev && dev->prvt); + + hri_sercomspi_write_DATA_reg(dev->prvt, data); + + return ERR_NONE; +} + +int32_t _spi_s_async_write_one(struct _spi_s_async_dev *dev, uint16_t data) +{ + ASSERT(dev && dev->prvt); + + hri_sercomspi_write_DATA_reg(dev->prvt, data); + + return ERR_NONE; +} + +int32_t _spi_s_sync_write_one(struct _spi_s_sync_dev *dev, uint16_t data) +{ + ASSERT(dev && dev->prvt); + + hri_sercomspi_write_DATA_reg(dev->prvt, data); + + return ERR_NONE; +} + +uint16_t _spi_m_async_read_one(struct _spi_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomspi_read_DATA_reg(dev->prvt); +} + +uint16_t _spi_s_async_read_one(struct _spi_s_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomspi_read_DATA_reg(dev->prvt); +} + +uint16_t _spi_s_sync_read_one(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomspi_read_DATA_reg(dev->prvt); +} + +int32_t _spi_m_async_register_callback(struct _spi_async_dev *dev, const enum _spi_async_dev_cb_type cb_type, + const FUNC_PTR func) +{ + typedef void (*func_t)(void); + struct _spi_async_dev *spid = dev; + + ASSERT(dev && (cb_type < SPI_DEV_CB_N)); + + func_t *p_ls = (func_t *)&spid->callbacks; + p_ls[cb_type] = (func_t)func; + + return ERR_NONE; +} + +int32_t _spi_s_async_register_callback(struct _spi_s_async_dev *dev, const enum _spi_s_async_dev_cb_type cb_type, + const FUNC_PTR func) +{ + return _spi_m_async_register_callback(dev, cb_type, func); +} + +bool _spi_s_sync_is_tx_ready(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomi2cm_get_INTFLAG_reg(dev->prvt, SERCOM_SPI_INTFLAG_DRE); +} + +bool _spi_s_sync_is_rx_ready(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomi2cm_get_INTFLAG_reg(dev->prvt, SERCOM_SPI_INTFLAG_RXC); +} + +bool _spi_s_sync_is_ss_deactivated(struct _spi_s_sync_dev *dev) +{ + void *hw = dev->prvt; + + ASSERT(dev && hw); + + if (hri_sercomi2cm_get_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_TXC)) { + hri_sercomspi_clear_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_TXC); + return true; + } + return false; +} + +bool _spi_s_sync_is_error(struct _spi_s_sync_dev *dev) +{ + void *hw = dev->prvt; + + ASSERT(dev && hw); + + if (hri_sercomi2cm_get_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_ERROR)) { + hri_sercomspi_clear_STATUS_reg(hw, SERCOM_SPI_STATUS_BUFOVF); + hri_sercomspi_clear_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_ERROR); + return true; + } + return false; +} + +/** + * \brief Enable/disable SPI master interrupt + * + * param[in] device The pointer to SPI master device instance + * param[in] type The type of interrupt to disable/enable if applicable + * param[in] state Enable or disable + */ +void _spi_m_async_set_irq_state(struct _spi_async_dev *const device, const enum _spi_async_dev_cb_type type, + const bool state) +{ + ASSERT(device); + + if (SPI_DEV_CB_COMPLETE == type) { + hri_sercomspi_write_INTEN_ERROR_bit(device->prvt, state); + } +} diff --git a/atmel-samd/asf4/samd51/hpl/systick/hpl_systick.c b/atmel-samd/asf4/samd51/hpl/systick/hpl_systick.c new file mode 100644 index 000000000..5ea3aac34 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/systick/hpl_systick.c @@ -0,0 +1,117 @@ + +/** + * \file + * + * \brief SysTick related functionality implementation. + * + * Copyright (C) 2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_time_measure.h> +#include <compiler.h> +#include <hpl_systick_config.h> +#ifdef _UNIT_TEST_ +#include <hri_systick_ARMv7_mock.h> +#endif + +/** + * \brief Initialize system time module + */ +void _system_time_init(void *const hw) +{ + (void)hw; + SysTick->LOAD = (0xFFFFFF << SysTick_LOAD_RELOAD_Pos); + SysTick->CTRL = (1 << SysTick_CTRL_ENABLE_Pos) | (CONF_SYSTICK_TICKINT << SysTick_CTRL_TICKINT_Pos) + | (1 << SysTick_CTRL_CLKSOURCE_Pos); +} +/** + * \brief Initialize delay functionality + */ +void _delay_init(void *const hw) +{ + _system_time_init(hw); +} + +/** + * \brief De-initialize system time module + */ +void _system_time_deinit(void *const hw) +{ + (void)hw; + SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; +} + +/** + * \brief Get system time + */ +system_time_t _system_time_get(const void *const hw) +{ + (void)hw; + return (system_time_t)SysTick->VAL; +} + +/** + * \brief Get maximum possible system time + */ +system_time_t _system_time_get_max_time_value(const void *const hw) +{ + (void)hw; + return 0xFFFFFF; +} +/** + * \brief Delay loop to delay n number of cycles + */ +void _delay_cycles(void *const hw, uint32_t cycles) +{ + (void)hw; + uint8_t n = cycles >> 24; + uint32_t buf = cycles; + + while (n--) { + SysTick->LOAD = 0xFFFFFF; + SysTick->VAL = 0xFFFFFF; + while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)) + ; + buf -= 0xFFFFFF; + } + + SysTick->LOAD = buf; + SysTick->VAL = buf; + while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)) + ; +} diff --git a/atmel-samd/asf4/samd51/hpl/tc/hpl_tc.c b/atmel-samd/asf4/samd51/hpl/tc/hpl_tc.c new file mode 100644 index 000000000..a897d8f39 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/tc/hpl_tc.c @@ -0,0 +1,377 @@ + +/** + * \file + * + * \brief SAM TC + * + * Copyright (C) 2014 - 2017 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <hpl_pwm.h> +#include <hpl_tc_config.h> +#include <hpl_timer.h> +#include <utils.h> +#include <utils_assert.h> +#include <hpl_tc_base.h> + +#ifndef CONF_TC0_ENABLE +#define CONF_TC0_ENABLE 0 +#endif +#ifndef CONF_TC1_ENABLE +#define CONF_TC1_ENABLE 0 +#endif +#ifndef CONF_TC2_ENABLE +#define CONF_TC2_ENABLE 0 +#endif +#ifndef CONF_TC3_ENABLE +#define CONF_TC3_ENABLE 0 +#endif +#ifndef CONF_TC4_ENABLE +#define CONF_TC4_ENABLE 0 +#endif +#ifndef CONF_TC5_ENABLE +#define CONF_TC5_ENABLE 0 +#endif +#ifndef CONF_TC6_ENABLE +#define CONF_TC6_ENABLE 0 +#endif +#ifndef CONF_TC7_ENABLE +#define CONF_TC7_ENABLE 0 +#endif + +/** + * \brief Macro is used to fill usart configuration structure based on its + * number + * + * \param[in] n The number of structures + */ +#define TC_CONFIGURATION(n) \ + { \ + n, TC##n##_IRQn, \ + TC_CTRLA_MODE(CONF_TC##n##_MODE) | TC_CTRLA_PRESCSYNC(CONF_TC##n##_PRESCSYNC) \ + | (CONF_TC##n##_RUNSTDBY << TC_CTRLA_RUNSTDBY_Pos) | (CONF_TC##n##_ONDEMAND << TC_CTRLA_ONDEMAND_Pos) \ + | TC_CTRLA_PRESCALER(CONF_TC##n##_PRESCALER) | (CONF_TC##n##_ALOCK << TC_CTRLA_ALOCK_Pos), \ + (CONF_TC##n##_OVFEO << TC_EVCTRL_OVFEO_Pos) | (CONF_TC##n##_TCEI << TC_EVCTRL_TCEI_Pos) \ + | (CONF_TC##n##_TCINV << TC_EVCTRL_TCINV_Pos) | (CONF_TC##n##_EVACT << TC_EVCTRL_EVACT_Pos) \ + | (CONF_TC##n##_MCEO0 << TC_EVCTRL_MCEO0_Pos) | (CONF_TC##n##_MCEO1 << TC_EVCTRL_MCEO1_Pos), \ + (CONF_TC##n##_DBGRUN << TC_DBGCTRL_DBGRUN_Pos), CONF_TC##n##_PER, CONF_TC##n##_CC0, CONF_TC##n##_CC1, \ + } +/** + * \brief TC configuration type + */ +struct tc_configuration { + uint8_t number; + IRQn_Type irq; + hri_tc_ctrla_reg_t ctrl_a; + hri_tc_evctrl_reg_t event_ctrl; + hri_tc_dbgctrl_reg_t dbg_ctrl; + hri_tc_per_reg_t per; + hri_tc_cc32_reg_t cc0; + hri_tc_cc32_reg_t cc1; +}; + +/** + * \brief Array of TC configurations + */ +static struct tc_configuration _tcs[] = { +#if CONF_TC0_ENABLE == 1 + TC_CONFIGURATION(0), +#endif +#if CONF_TC1_ENABLE == 1 + TC_CONFIGURATION(1), +#endif +#if CONF_TC2_ENABLE == 1 + TC_CONFIGURATION(2), +#endif +#if CONF_TC3_ENABLE == 1 + TC_CONFIGURATION(3), +#endif +#if CONF_TC4_ENABLE == 1 + TC_CONFIGURATION(4), +#endif +#if CONF_TC5_ENABLE == 1 + TC_CONFIGURATION(5), +#endif +#if CONF_TC6_ENABLE == 1 + TC_CONFIGURATION(6), +#endif +#if CONF_TC7_ENABLE == 1 + TC_CONFIGURATION(7), +#endif +}; + +static struct _pwm_device *_tc0_dev = NULL; + +static int8_t get_tc_index(const void *const hw); +static void _tc_init_irq_param(const void *const hw, void *dev); +static inline uint8_t _get_hardware_offset(const void *const hw); +/** + * \brief Initialize TC for PWM mode + */ +int32_t _pwm_init(struct _pwm_device *const device, void *const hw) +{ + int8_t i = get_tc_index(hw); + device->hw = hw; + + hri_tc_wait_for_sync(hw, TC_SYNCBUSY_SWRST); + if (hri_tc_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + hri_tc_set_CTRLA_SWRST_bit(hw); + hri_tc_wait_for_sync(hw, TC_SYNCBUSY_SWRST); + + hri_tc_write_CTRLA_reg(hw, _tcs[i].ctrl_a); + hri_tc_write_DBGCTRL_reg(hw, _tcs[i].dbg_ctrl); + hri_tc_write_EVCTRL_reg(hw, _tcs[i].event_ctrl); + hri_tc_write_WAVE_reg(hw, TC_WAVE_WAVEGEN_MPWM_Val); + + if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT32) { + hri_tccount32_write_CC_reg(hw, 0, _tcs[i].cc0); + hri_tccount32_write_CC_reg(hw, 1, _tcs[i].cc1); + } else if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT16) { + hri_tccount16_write_CC_reg(hw, 0, (hri_tc_count16_reg_t)_tcs[i].cc0); + hri_tccount16_write_CC_reg(hw, 1, (hri_tc_count16_reg_t)_tcs[i].cc1); + } else { + /* 8-bit resolution is not accepted by duty cycle control */ + return -1; + } + + _tc_init_irq_param(hw, (void *)device); + NVIC_DisableIRQ(_tcs[i].irq); + NVIC_ClearPendingIRQ(_tcs[i].irq); + NVIC_EnableIRQ(_tcs[i].irq); + + return 0; +} +/** + * \brief De-initialize TC for PWM mode + */ +void _pwm_deinit(struct _pwm_device *const device) +{ + void *const hw = device->hw; + int8_t i = get_tc_index(hw); + ASSERT(ARRAY_SIZE(_tcs)); + + NVIC_DisableIRQ(_tcs[i].irq); + + hri_tc_clear_CTRLA_ENABLE_bit(hw); + hri_tc_set_CTRLA_SWRST_bit(hw); +} +/** + * \brief Start PWM + */ +void _pwm_enable(struct _pwm_device *const device) +{ + hri_tc_set_CTRLA_ENABLE_bit(device->hw); +} +/** + * \brief Stop PWM + */ +void _pwm_disable(struct _pwm_device *const device) +{ + hri_tc_clear_CTRLA_ENABLE_bit(device->hw); +} +/** + * \brief Set PWM parameter + */ +void _pwm_set_param(struct _pwm_device *const device, const pwm_period_t period, const pwm_period_t duty_cycle) +{ + void *const hw = device->hw; + int8_t i = get_tc_index(hw); + + _tcs[i].cc0 = period; + _tcs[i].cc1 = duty_cycle; + if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT32) { + hri_tccount32_write_CC_reg(hw, 0, _tcs[i].cc0); + hri_tccount32_write_CC_reg(hw, 1, _tcs[i].cc1); + } else { + hri_tccount16_write_CC_reg(hw, 0, _tcs[i].cc0); + hri_tccount16_write_CC_reg(hw, 1, _tcs[i].cc1); + } +} +/** + * \brief Get pwm waveform period value + */ +pwm_period_t _pwm_get_period(const struct _pwm_device *const device) +{ + void *const hw = device->hw; + int8_t i = get_tc_index(hw); + + if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT32) { + return (pwm_period_t)(hri_tccount32_read_CC_reg(hw, 0)); + } else { + return (pwm_period_t)(hri_tccount16_read_CC_reg(hw, 0)); + } +} +/** + * \brief Get pwm waveform duty cycle + */ +uint32_t _pwm_get_duty(const struct _pwm_device *const device) +{ + void *const hw = device->hw; + int8_t i = get_tc_index(hw); + uint32_t per; + uint32_t duty_cycle; + + if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT32) { + per = hri_tccount32_read_CC_reg(hw, 0); + duty_cycle = hri_tccount32_read_CC_reg(hw, 1); + } else { + per = hri_tccount16_read_CC_reg(hw, 0); + duty_cycle = hri_tccount16_read_CC_reg(hw, 1); + } + + return ((duty_cycle * 1000) / per); +} +/** + * \brief Check if PWM is running + */ +bool _pwm_is_enabled(const struct _pwm_device *const device) +{ + return hri_tc_get_CTRLA_ENABLE_bit(device->hw); +} +/** + * \brief Enable/disable PWM interrupt + */ +void _pwm_set_irq_state(struct _pwm_device *const device, const enum _pwm_callback_type type, const bool disable) +{ + ASSERT(device); + + if (PWM_DEVICE_PERIOD_CB == type) { + hri_tc_write_INTEN_OVF_bit(device->hw, disable); + } else if (PWM_DEVICE_ERROR_CB == type) { + hri_tc_write_INTEN_ERR_bit(device->hw, disable); + } +} + +/** + * \brief Retrieve timer helper functions + */ +struct _timer_hpl_interface *_tc_get_timer(void) +{ + return NULL; +} + +/** + * \brief Retrieve pwm helper functions + */ +struct _pwm_hpl_interface *_tc_get_pwm(void) +{ + return NULL; +} +/** + * \internal TC interrupt handler for PWM + * + * \param[in] instance TC instance number + */ +static void tc_pwm_interrupt_handler(struct _pwm_device *device) +{ + void *const hw = device->hw; + + if (hri_tc_get_interrupt_OVF_bit(hw)) { + hri_tc_clear_interrupt_OVF_bit(hw); + if (NULL != device->callback.pwm_period_cb) { + device->callback.pwm_period_cb(device); + } + } + if (hri_tc_get_INTEN_ERR_bit(hw)) { + hri_tc_clear_interrupt_ERR_bit(hw); + if (NULL != device->callback.pwm_error_cb) { + device->callback.pwm_error_cb(device); + } + } +} + +/** +* \brief TC interrupt handler +*/ +void TC0_Handler(void) +{ + tc_pwm_interrupt_handler(_tc0_dev); +} + +/** + * \internal Retrieve TC index + * + * \param[in] hw The pointer to hardware instance + * + * \return The index of TC configuration + */ +static int8_t get_tc_index(const void *const hw) +{ + uint8_t index = _get_hardware_offset(hw); + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_tcs); i++) { + if (_tcs[i].number == index) { + return i; + } + } + + ASSERT(false); + return -1; +} + +/** + * \brief Init irq param with the given tc hardware instance + */ +static void _tc_init_irq_param(const void *const hw, void *dev) +{ + if (hw == TC0) { + _tc0_dev = (struct _pwm_device *)dev; + } +} + +/** + * \internal Retrieve TC hardware index + * + * \param[in] hw The pointer to hardware instance + */ +static inline uint8_t _get_hardware_offset(const void *const hw) +{ + /* List of available TC modules. */ + Tc *const tc_modules[TC_INST_NUM] = TC_INSTS; + + /* Find index for TC instance. */ + for (uint32_t i = 0; i < TC_INST_NUM; i++) { + if ((uint32_t)hw == (uint32_t)tc_modules[i]) { + return i; + } + } + return 0; +} diff --git a/atmel-samd/asf4/samd51/hpl/tc/hpl_tc_base.h b/atmel-samd/asf4/samd51/hpl/tc/hpl_tc_base.h new file mode 100644 index 000000000..9d7f2c72a --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/tc/hpl_tc_base.h @@ -0,0 +1,87 @@ +/** + * \file + * + * \brief SAM Timer/Counter + * + * Copyright (C) 2014 - 2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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 distributionn. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + */ + +#ifndef _HPL_TC_BASE_H_INCLUDED +#define _HPL_TC_BASE_H_INCLUDED + +#include <hpl_timer.h> +#include <hpl_pwm.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup tc_group TC Hardware Proxy Layer + * + * \section tc_hpl_rev Revision History + * - v0.0.0.1 Initial Commit + * + *@{ + */ + +/** + * \name HPL functions + */ +//@{ + +/** + * \brief Retrieve timer helper functions + * + * \return A pointer to set of timer helper functions + */ +struct _timer_hpl_interface *_tc_get_timer(void); + +/** + * \brief Retrieve pwm helper functions + * + * \return A pointer to set of pwm helper functions + */ +struct _pwm_hpl_interface *_tc_get_pwm(void); + +//@} +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif /* _HPL_TC_BASE_H_INCLUDED */ diff --git a/atmel-samd/asf4/samd51/hpl/trng/hpl_trng.c b/atmel-samd/asf4/samd51/hpl/trng/hpl_trng.c new file mode 100644 index 000000000..2b1f35a16 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/trng/hpl_trng.c @@ -0,0 +1,120 @@ +/** + * \file + * + * \brief True Random Number Generator + * + * Copyright (C) 2015-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <err_codes.h> +#include <hpl_rand_sync.h> +#include <hpl_trng_config.h> +#include <utils_assert.h> + +static inline int32_t _trng_init(void *hw) +{ + if (hri_trng_get_CTRLA_reg(hw, TRNG_CTRLA_ENABLE)) { + return ERR_DENIED; + } + if (CONF_TRNG_RUNSTDBY) { + hri_trng_set_CTRLA_RUNSTDBY_bit(hw); + } else { + hri_trng_clear_CTRLA_RUNSTDBY_bit(hw); + } + if (CONF_TRNG_DATARDYEO) { + hri_trng_set_EVCTRL_DATARDYEO_bit(hw); + } else { + hri_trng_clear_EVCTRL_DATARDYEO_bit(hw); + } + return ERR_NONE; +} + +int32_t _rand_sync_init(struct _rand_sync_dev *const dev, void *const hw) +{ + int32_t rc; + + ASSERT(dev && hw); + + rc = _trng_init(hw); + if (rc == ERR_NONE) { + dev->prvt = hw; + dev->n_bits = 32; + } + return rc; +} + +void _rand_sync_deinit(struct _rand_sync_dev *const dev) +{ + _rand_sync_disable(dev); +} + +int32_t _rand_sync_enable(struct _rand_sync_dev *const dev) +{ + ASSERT(dev); + ASSERT(dev->prvt); + + hri_trng_set_CTRLA_ENABLE_bit(dev->prvt); + return ERR_NONE; +} + +void _rand_sync_disable(struct _rand_sync_dev *const dev) +{ + ASSERT(dev); + ASSERT(dev->prvt); + + hri_trng_clear_CTRLA_ENABLE_bit(dev->prvt); +} + +int32_t _rand_sync_set_seed(struct _rand_sync_dev *const dev, const uint32_t seed) +{ + (void)dev; + (void)seed; + return ERR_UNSUPPORTED_OP; +} + +uint32_t _rand_sync_read_one(const struct _rand_sync_dev *const dev) +{ + ASSERT(dev); + ASSERT(dev->prvt); + ASSERT(hri_trng_get_CTRLA_reg(dev->prvt, TRNG_CTRLA_ENABLE)); + + while (!hri_trng_get_INTFLAG_reg(dev->prvt, TRNG_INTFLAG_DATARDY)) { + /* Wait until data ready. */ + } + return hri_trng_read_DATA_reg(dev->prvt); +} diff --git a/atmel-samd/asf4/samd51/hpl/usb/hpl_usb.c b/atmel-samd/asf4/samd51/hpl/usb/hpl_usb.c new file mode 100644 index 000000000..bdf245aa5 --- /dev/null +++ b/atmel-samd/asf4/samd51/hpl/usb/hpl_usb.c @@ -0,0 +1,2088 @@ +/** + * \file + * + * \brief SAM USB HPL + * + * Copyright (C) 2015-2017 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#include <compiler.h> +#include <hal_atomic.h> +#include <hpl_usb.h> +#include <hpl_usb_device.h> + +#include <hpl_usb_config.h> +#include <string.h> +#include <utils_assert.h> + +/** + * \brief Dummy callback function + * \return Always false. + */ +static bool _dummy_func_no_return(uint32_t unused0, uint32_t unused1) +{ + (void)unused0; + (void)unused1; + return false; +} + +/** + * \brief Load USB calibration value from NVM + */ +static void _usb_load_calib(void) +{ +#define NVM_USB_PAD_TRANSN_POS 32 +#define NVM_USB_PAD_TRANSN_SIZE 5 +#define NVM_USB_PAD_TRANSP_POS 37 +#define NVM_USB_PAD_TRANSP_SIZE 5 +#define NVM_USB_PAD_TRIM_POS 42 +#define NVM_USB_PAD_TRIM_SIZE 3 + Usb * hw = USB; + uint32_t pad_transn + = (*((uint32_t *)(NVMCTRL_SW0) + (NVM_USB_PAD_TRANSN_POS / 32)) >> (NVM_USB_PAD_TRANSN_POS % 32)) + & ((1 << NVM_USB_PAD_TRANSN_SIZE) - 1); + uint32_t pad_transp + = (*((uint32_t *)(NVMCTRL_SW0) + (NVM_USB_PAD_TRANSP_POS / 32)) >> (NVM_USB_PAD_TRANSP_POS % 32)) + & ((1 << NVM_USB_PAD_TRANSP_SIZE) - 1); + uint32_t pad_trim = (*((uint32_t *)(NVMCTRL_SW0) + (NVM_USB_PAD_TRIM_POS / 32)) >> (NVM_USB_PAD_TRIM_POS % 32)) + & ((1 << NVM_USB_PAD_TRIM_SIZE) - 1); + if (pad_transn == 0 || pad_transn == 0x1F) { + pad_transn = 9; + } + if (pad_transp == 0 || pad_transp == 0x1F) { + pad_transp = 25; + } + if (pad_trim == 0 || pad_trim == 0x7) { + pad_trim = 6; + } + + hw->DEVICE.PADCAL.reg = USB_PADCAL_TRANSN(pad_transn) | USB_PADCAL_TRANSP(pad_transp) | USB_PADCAL_TRIM(pad_trim); + + hw->DEVICE.QOSCTRL.bit.CQOS = 3; + hw->DEVICE.QOSCTRL.bit.DQOS = 3; +} + +/** \name USB clock source management */ +/*@{*/ + +/** USB clock is generated by DFLL. */ +#define USB_CLK_SRC_DFLL 0 + +/** USB clock is generated by DPLL. */ +#define USB_CLK_SRC_DPLL 1 + +/** Uses DFLL as USB clock source. */ +#define CONF_USB_D_CLK_SRC USB_CLK_SRC_DFLL + +/** Retry for USB remote wakeup sending. */ +#define CONF_USB_RMT_WKUP_RETRY 5 + +/** + * \brief Wait DPLL clock to be ready + */ +static inline void _usb_d_dev_wait_dpll_rdy(void) +{ +#define DPLL_READY_FLAG (OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK) + while (hri_oscctrl_get_DPLLSTATUS_reg(OSCCTRL, 0, DPLL_READY_FLAG) != DPLL_READY_FLAG) + ; +} + +/** + * \brief Wait DFLL clock to be ready + */ +static inline void _usb_d_dev_wait_dfll_rdy(void) +{ + if (hri_oscctrl_get_DFLLCTRLB_MODE_bit(OSCCTRL)) { + while (hri_oscctrl_get_STATUS_reg(OSCCTRL, (OSCCTRL_STATUS_DFLLRDY | OSCCTRL_STATUS_DFLLLCKC)) + != (OSCCTRL_STATUS_DFLLRDY | OSCCTRL_STATUS_DFLLLCKC)) + ; + } else { + while (hri_oscctrl_get_STATUS_reg(OSCCTRL, OSCCTRL_STATUS_DFLLRDY) != OSCCTRL_STATUS_DFLLRDY) + ; + } +} + +/** + * \brief Wait USB source clock to be ready + * \param[in] clk_src Clock source, could be \ref USB_CLK_SRC_DFLL or + * \ref USB_CLK_SRC_DPLL. + */ +static inline void _usb_d_dev_wait_clk_rdy(const uint8_t clk_src) +{ + if (clk_src == USB_CLK_SRC_DFLL) { + _usb_d_dev_wait_dfll_rdy(); + } else if (clk_src == USB_CLK_SRC_DPLL) { + _usb_d_dev_wait_dpll_rdy(); + } +} + +/*@}*/ + +/** \name USB general settings */ +/*@{*/ + +/** Increase the value to be aligned. */ +#define _usb_align_up(val) (((val)&0x3) ? (((val) + 4 - ((val)&0x3))) : (val)) + +/** Check if the buffer is in RAM (can DMA), or cache needed + * \param[in] a Buffer start address. + * \param[in] s Buffer size, in number of bytes. + * \return \c true If the buffer is in RAM. + */ +#define _IN_RAM(a, s) ((0x20000000 <= (uint32_t)(a)) && (((uint32_t)(a) + (s)) < 0x20008000)) + +/** Check if the address should be placed in RAM. */ +#define _usb_is_addr4dma(addr, size) _IN_RAM((addr), (size)) + +/** Check if the address is 32-bit aligned. */ +#define _usb_is_aligned(val) (((uint32_t)(val)&0x3) == 0) +/*@}*/ + +/* Cache static configurations. + * By default, all OUT endpoint have 64 bytes cache. */ +#ifndef CONF_USB_EP0_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP0_CACHE 64 +#endif + +#ifndef CONF_USB_EP0_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP0_I_CACHE 0 +#endif + +#ifndef CONF_USB_EP1_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP1_CACHE 64 +#endif + +#ifndef CONF_USB_EP1_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP1_I_CACHE 0 +#endif + +#ifndef CONF_USB_EP2_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP2_CACHE 64 +#endif + +#ifndef CONF_USB_EP2_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP2_I_CACHE 0 +#endif + +#ifndef CONF_USB_EP3_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP3_CACHE 64 +#endif + +#ifndef CONF_USB_EP3_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP3_I_CACHE 0 +#endif + +#ifndef CONF_USB_EP4_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP4_CACHE 64 +#endif + +#ifndef CONF_USB_EP4_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP4_I_CACHE 0 +#endif + +#ifndef CONF_USB_EP5_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP5_CACHE 64 +#endif + +#ifndef CONF_USB_EP5_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP5_I_CACHE 0 +#endif + +#ifndef CONF_USB_EP6_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP6_CACHE 64 +#endif + +#ifndef CONF_USB_EP6_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP6_I_CACHE 0 +#endif + +#ifndef CONF_USB_EP7_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP7_CACHE 64 +#endif + +#ifndef CONF_USB_EP7_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP7_I_CACHE 0 +#endif + +#ifndef CONF_USB_EP8_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP8_CACHE 64 +#endif + +#ifndef CONF_USB_EP8_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP8_I_CACHE 0 +#endif + +#ifndef CONF_USB_EP9_CACHE +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#define CONF_USB_EP9_CACHE 64 +#endif + +#ifndef CONF_USB_EP9_I_CACHE +/** Endpoint cache buffer for IN transactions (none-control). */ +#define CONF_USB_EP9_I_CACHE 0 +#endif + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP0_CACHE +static uint32_t _usb_ep0_cache[_usb_align_up(CONF_USB_EP0_CACHE) / 4]; +#else +#define _usb_ep0_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#define _usb_ep0_i_cache NULL + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP1_CACHE && CONF_USB_D_MAX_EP_N >= 1 +uint32_t _usb_ep1_cache[_usb_align_up(CONF_USB_EP1_CACHE) / 4]; +#else +#define _usb_ep1_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#if CONF_USB_EP1_I_CACHE && CONF_USB_D_MAX_EP_N >= 1 +uint32_t _usb_ep1_i_cache[_usb_align_up(CONF_USB_EP1_I_CACHE) / 4]; +#else +#define _usb_ep1_i_cache NULL +#endif + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP2_CACHE && CONF_USB_D_MAX_EP_N >= 2 +static uint32_t _usb_ep2_cache[_usb_align_up(CONF_USB_EP2_CACHE) / 4]; +#else +#define _usb_ep2_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#if CONF_USB_EP2_I_CACHE && CONF_USB_D_MAX_EP_N >= 2 +static uint32_t _usb_ep2_i_cache[_usb_align_up(CONF_USB_EP2_I_CACHE) / 4]; +#else +#define _usb_ep2_i_cache NULL +#endif + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP3_CACHE && CONF_USB_D_MAX_EP_N >= 3 +static uint32_t _usb_ep3_cache[_usb_align_up(CONF_USB_EP3_CACHE) / 4]; +#else +#define _usb_ep3_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#if CONF_USB_EP3_I_CACHE && CONF_USB_D_MAX_EP_N >= 3 +static uint32_t _usb_ep3_i_cache[_usb_align_up(CONF_USB_EP3_I_CACHE) / 4]; +#else +#define _usb_ep3_i_cache NULL +#endif + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP4_CACHE && CONF_USB_D_MAX_EP_N >= 4 +static uint32_t _usb_ep4_cache[_usb_align_up(CONF_USB_EP4_CACHE) / 4]; +#else +#define _usb_ep4_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#if CONF_USB_EP4_I_CACHE && CONF_USB_D_MAX_EP_N >= 4 +static uint32_t _usb_ep4_i_cache[_usb_align_up(CONF_USB_EP4_I_CACHE) / 4]; +#else +#define _usb_ep4_i_cache NULL +#endif + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP5_CACHE && CONF_USB_D_MAX_EP_N >= 5 +static uint32_t _usb_ep5_cache[_usb_align_up(CONF_USB_EP5_CACHE) / 4]; +#else +#define _usb_ep5_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#if CONF_USB_EP5_I_CACHE && CONF_USB_D_MAX_EP_N >= 5 +static uint32_t _usb_ep5_i_cache[_usb_align_up(CONF_USB_EP5_I_CACHE) / 4]; +#else +#define _usb_ep5_i_cache NULL +#endif + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP6_CACHE && CONF_USB_D_MAX_EP_N >= 6 +static uint32_t _usb_ep6_cache[_usb_align_up(CONF_USB_EP6_CACHE) / 4]; +#else +#define _usb_ep6_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#if CONF_USB_EP6_I_CACHE && CONF_USB_D_MAX_EP_N >= 6 +static uint32_t _usb_ep6_i_cache[_usb_align_up(CONF_USB_EP6_I_CACHE) / 4]; +#else +#define _usb_ep6_i_cache NULL +#endif + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP7_CACHE && CONF_USB_D_MAX_EP_N >= 7 +static uint32_t _usb_ep7_cache[_usb_align_up(CONF_USB_EP7_CACHE) / 4]; +#else +#define _usb_ep7_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#if CONF_USB_EP7_I_CACHE && CONF_USB_D_MAX_EP_N >= 7 +static uint32_t _usb_ep7_i_cache[_usb_align_up(CONF_USB_EP7_I_CACHE) / 4]; +#else +#define _usb_ep7_i_cache NULL +#endif + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP8_CACHE && CONF_USB_D_MAX_EP_N >= 8 +static uint32_t _usb_ep8_cache[_usb_align_up(CONF_USB_EP8_CACHE) / 4]; +#else +#define _usb_ep8_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#if CONF_USB_EP8_I_CACHE && CONF_USB_D_MAX_EP_N >= 8 +static uint32_t _usb_ep8_i_cache[_usb_align_up(CONF_USB_EP8_I_CACHE) / 4]; +#else +#define _usb_ep8_i_cache NULL +#endif + +/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT + * transactions (control). */ +#if CONF_USB_EP9_CACHE && CONF_USB_D_MAX_EP_N >= 9 +static uint32_t _usb_ep9_cache[_usb_align_up(CONF_USB_EP9_CACHE) / 4]; +#else +#define _usb_ep9_cache NULL +#endif + +/** Endpoint cache buffer for IN transactions (none-control). */ +#if CONF_USB_EP9_I_CACHE && CONF_USB_D_MAX_EP_N >= 9 +static uint32_t _usb_ep9_i_cache[_usb_align_up(CONF_USB_EP9_I_CACHE) / 4]; +#else +#define _usb_ep9_i_cache NULL +#endif + +/** Access endpoint cache buffer for OUT transactions (none-control) or + * SETUP/IN/OUT transactions (control). */ +#define _USB_EP_CACHE(n) ((void *)_usb_ep##n##_cache) + +/** Access endpoint cache buffer for IN transactions (none-control). */ +#define _USB_EP_I_CACHE(n) ((void *)_usb_ep##n##_i_cache) + +/** The configuration settings for one of the endpoint hardware. */ +struct _usb_ep_cfg_item { + /* Endpoint cache buffer for OUT transactions (none-control) or + * SETUP/IN/OUT transactions (control). */ + void *cache; + /* endpoint cache buffer for IN transactions (none-control). */ + void *i_cache; + /* Cache buffer size for OUT transactions (none-control) or + * SETUP/IN/OUT transactions (control). */ + uint16_t size; + /* Cache buffer size for IN transactions (none-control). */ + uint16_t i_size; +}; + +/** Build the endpoint configuration settings for one endpoint. */ +#define _USB_EP_CFG_ITEM(n) \ + { \ + _USB_EP_CACHE(n), _USB_EP_I_CACHE(n), CONF_USB_EP##n##_CACHE, CONF_USB_EP##n##_I_CACHE, \ + } + +/** The configuration settings for all endpoint. */ +static const struct _usb_ep_cfg_item _usb_ep_cfgs[] = {_USB_EP_CFG_ITEM(0) +#if CONF_USB_D_MAX_EP_N >= 1 + , + _USB_EP_CFG_ITEM(1) +#endif +#if CONF_USB_D_MAX_EP_N >= 2 + , + _USB_EP_CFG_ITEM(2) +#endif +#if CONF_USB_D_MAX_EP_N >= 3 + , + _USB_EP_CFG_ITEM(3) +#endif +#if CONF_USB_D_MAX_EP_N >= 4 + , + _USB_EP_CFG_ITEM(4) +#endif +#if CONF_USB_D_MAX_EP_N >= 5 + , + _USB_EP_CFG_ITEM(5) +#endif +#if CONF_USB_D_MAX_EP_N >= 6 + , + _USB_EP_CFG_ITEM(6) +#endif +#if CONF_USB_D_MAX_EP_N >= 7 + , + _USB_EP_CFG_ITEM(7) +#endif +#if CONF_USB_D_MAX_EP_N >= 8 + , + _USB_EP_CFG_ITEM(8) +#endif +#if CONF_USB_D_MAX_EP_N >= 9 + , + _USB_EP_CFG_ITEM(9) +#endif +}; + +/** \name HW specific settings and implements */ +/*@{*/ + +/** Number of endpoints supported. */ +#define USB_D_N_EP (1 + CONF_USB_D_NUM_EP_SP * 2) + +/** HPL USB device endpoint struct. */ +struct _usb_d_dev_ep { + /** Pointer to transaction buffer. */ + uint8_t *trans_buf; + /** Transaction size. */ + uint32_t trans_size; + /** Transaction transferred count. */ + uint32_t trans_count; + + /** Pointer to cache buffer, must be aligned. */ + uint8_t *cache; + + /** Endpoint size. */ + uint16_t size; + /** Endpoint address. */ + uint8_t ep; + /** Feature flags. */ + union { + /** Interpreted by bit fields. */ + struct { + /** EPCFG.ETYPE. */ + uint8_t eptype : 3; + /** Stall status. */ + uint8_t is_stalled : 1; + /** Transaction auto ZLP. */ + uint8_t need_zlp : 1; + /** Transaction with cache */ + uint8_t use_cache : 1; + /** Endpoint is busy. */ + uint8_t is_busy : 1; + /** Transaction direction. */ + uint8_t dir : 1; + } bits; + uint8_t u8; + } flags; +}; + +/** Check if the endpoint is used. */ +#define _usb_d_dev_ep_is_used(ept) ((ept)->ep != 0xFF) + +/** Check if the endpoint is busy doing transactions. */ +#define _usb_d_dev_ep_is_busy(ept) ((ept)->flags.bits.is_busy) + +/** Check if the endpoint is control endpoint. */ +#define _usb_d_dev_ep_is_ctrl(ept) ((ept)->flags.bits.eptype == USB_D_EPTYPE_CTRL) + +/** Check if the endpoint transactions are IN. */ +#define _usb_d_dev_ep_is_in(ept) ((ept)->flags.bits.dir) + +/** Interrupt flags for SETUP transaction. */ +#define USB_D_SETUP_INT_FLAGS (USB_DEVICE_EPINTFLAG_RXSTP) + +/** Interrupt flags for BANK1 transactions. */ +#define USB_D_BANK1_INT_FLAGS (USB_DEVICE_EPINTFLAG_TRCPT1 | USB_DEVICE_EPINTFLAG_TRFAIL1 | USB_DEVICE_EPINTFLAG_STALL1) + +/** Interrupt flags for BANK0 transactions. */ +#define USB_D_BANK0_INT_FLAGS (USB_DEVICE_EPINTFLAG_TRCPT0 | USB_DEVICE_EPINTFLAG_TRFAIL0 | USB_DEVICE_EPINTFLAG_STALL0) + +/** Interrupt flags for SETUP/IN/OUT transactions. */ +#define USB_D_ALL_INT_FLAGS (0x7F) + +/** Interrupt flags for WAKEUP event. */ +#define USB_D_WAKEUP_INT_FLAGS (USB_DEVICE_INTFLAG_UPRSM | USB_DEVICE_INTFLAG_EORSM | USB_DEVICE_INTFLAG_WAKEUP) + +/** Interrupt flags for SUSPEND event. */ +#define USB_D_SUSPEND_INT_FLAGS (USB_DEVICE_INTFLAG_LPMSUSP | USB_DEVICE_INTFLAG_SUSPEND) + +/** Max data bytes for a single DMA transfer. */ +#define USB_D_DEV_TRANS_MAX 8192 /* 14-bits, uses 13-bits. */ + +/** Endpoint type setting to disable. */ +#define USB_D_EPTYPE_DISABLE 0 + +/** Endpoint type setting to work as control endpoint. */ +#define USB_D_EPTYPE_CTRL 1 + +/** Endpoint type setting to work as isochronous endpoint. */ +#define USB_D_EPTYPE_ISOCH 2 + +/** Endpoint type setting to work as interrupt endpoint. */ +#define USB_D_EPTYPE_INT 3 + +/** Endpoint type setting to work as bulk endpoint. */ +#define USB_D_EPTYPE_BULK 4 + +/** Endpoint type setting for dual bank endpoint. */ +#define USB_D_EPTYPE_DUAL 5 + +/** EPCFG register value for control endpoints. */ +#define USB_D_EPCFG_CTRL 0x11 + +/** HPL USB device struct. */ +struct _usb_d_dev { + /** Callbacks of USB device. */ + struct _usb_d_dev_callbacks callbacks; + /** Endpoint transaction callbacks. */ + struct _usb_d_dev_ep_callbacks ep_callbacks; + /** Endpoints (ep0 + others). */ + struct _usb_d_dev_ep ep[USB_D_N_EP]; +}; + +/** Private data for SAM0 USB peripheral. + */ +typedef struct _usb_d_dev_prvt { + /** USB device descriptor table for peripheral to work. */ + UsbDeviceDescriptor desc_table[CONF_USB_D_MAX_EP_N + 1]; +} usb_d_dev_prvt_t; + +/*@}*/ + +/** USB device driver instance. */ +static struct _usb_d_dev dev_inst; + +/** USB device driver private data instance. */ +static struct _usb_d_dev_prvt prvt_inst; + +static void _usb_d_dev_reset_epts(void); + +static void _usb_d_dev_trans_done(struct _usb_d_dev_ep *ept, const int32_t status); +static void _usb_d_dev_trans_stop(struct _usb_d_dev_ep *ept, bool dir, const int32_t code); + +static void _usb_d_dev_in_next(struct _usb_d_dev_ep *ept, bool isr); +static void _usb_d_dev_out_next(struct _usb_d_dev_ep *ept, bool isr); + +static inline void _usb_d_dev_trans_setup(struct _usb_d_dev_ep *ept); + +/** \brief ACK the endpoint interrupt + * \param[in] epn Endpoint number. + * \param[in] flags Interrupt flags. + */ +static inline void _usbd_ep_int_ack(uint8_t epn, uint32_t flags) +{ + hri_usbendpoint_clear_EPINTFLAG_reg(USB, epn, flags); +} + +/** \brief Enable the endpoint interrupt + * \param[in] epn Endpoint number. + * \param[in] flags Interrupt flags. + */ +static inline void _usbd_ep_int_en(uint8_t epn, uint32_t flags) +{ + hri_usbendpoint_set_EPINTEN_reg(USB, epn, flags); +} + +/** \brief Disable the endpoint interrupt + * \param[in] epn Endpoint number. + * \param[in] flags Interrupt flags. + */ +static inline void _usbd_ep_int_dis(uint8_t epn, uint32_t flags) +{ + hri_usbendpoint_clear_EPINTEN_reg(USB, epn, flags); +} + +/** \brief Check if endpoint is control endpoint + * \param[in] epn Endpoint number. + */ +static inline bool _usbd_ep_is_ctrl(uint8_t epn) +{ + return (hri_usbendpoint_read_EPCFG_reg(USB, epn) == USB_D_EPCFG_CTRL); +} + +/** \brief Set endpoint stall + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] st Stall status. + */ +static inline void _usbd_ep_set_stall(uint8_t epn, uint8_t bank_n, bool st) +{ + if (st) { + hri_usbendpoint_set_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_STALLRQ0 << bank_n)); + } else { + hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_STALLRQ0 << bank_n)); + } +} + +/** \brief Check if the endpoint is stalled + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \return \c true if it's stalled. + */ +static inline bool _usbd_ep_is_stalled(uint8_t epn, uint8_t bank_n) +{ + Usb *hw = USB; + return (hri_usbendpoint_read_EPSTATUS_reg(hw, epn) & (USB_DEVICE_EPSTATUS_STALLRQ0 << bank_n)); +} + +/** \brief Check if stall has been sent from the endpoint + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \return \c true if it's sent. + */ +static inline bool _usbd_ep_is_stall_sent(uint8_t epn, uint8_t bank_n) +{ + Usb *hw = USB; + return (hri_usbendpoint_read_EPINTFLAG_reg(hw, epn) & (USB_DEVICE_EPINTFLAG_STALL0 << bank_n)); +} + +/** \brief ACK endpoint STALL interrupt + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + */ +static inline void _usbd_ep_ack_stall(uint8_t epn, uint8_t bank_n) +{ + _usbd_ep_int_ack(epn, (USB_DEVICE_EPINTFLAG_STALL0 << bank_n)); +} + +/** \brief Enable/disable endpoint STALL interrupt + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] en \c true to enable, \c false to disable. + */ +static inline void _usbd_ep_int_stall_en(uint8_t epn, uint8_t bank_n, const bool en) +{ + if (en) { + _usbd_ep_int_en(epn, USB_DEVICE_EPINTFLAG_STALL0 << bank_n); + } else { + _usbd_ep_int_dis(epn, USB_DEVICE_EPINTFLAG_STALL0 << bank_n); + } +} + +/** \brief Stop SETUP transactions + * \param[in] epn Endpoint number. + */ +static inline void _usbd_ep_stop_setup(uint8_t epn) +{ + hri_usbendpoint_clear_EPINTEN_RXSTP_bit(USB, epn); +} + +/** \brief Check if SETUP packet is ready in cache + * \param[in] epn Endpoint number. + */ +static inline bool _usbd_ep_is_setup(uint8_t epn) +{ + return hri_usbendpoint_get_EPINTFLAG_reg(USB, epn, USB_DEVICE_EPINTFLAG_RXSTP); +} + +/** \brief ACK endpoint SETUP interrupt + * \param[in] epn Endpoint number. + */ +static inline void _usbd_ep_ack_setup(uint8_t epn) +{ + _usbd_ep_int_ack(epn, USB_DEVICE_EPINTFLAG_RXSTP); +} + +/** \brief Set endpoint toggle value + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] tgl Toggle value. + */ +static inline void _usbd_ep_set_toggle(uint8_t epn, uint8_t bank_n, uint8_t tgl) +{ + if (tgl) { + hri_usbendpoint_set_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_DTGLOUT << bank_n)); + } else { + hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_DTGLOUT << bank_n)); + } +} + +/** \brief ACK IN/OUT complete interrupt + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + */ +static inline void _usbd_ep_ack_io_cpt(uint8_t epn, uint8_t bank_n) +{ + _usbd_ep_int_ack(epn, USB_DEVICE_EPINTFLAG_TRCPT0 << bank_n); +} + +/** \brief Set DMA buffer used for bank data + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] addr DMA buffer address to set. + */ +static inline void _usbd_ep_set_buf(uint8_t epn, uint8_t bank_n, uint32_t addr) +{ + UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n]; + bank->ADDR.reg = addr; +} + +/** \brief Set bank count for IN transactions + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] count Data count for IN. + */ +static inline void _usbd_ep_set_in_count(uint8_t epn, uint8_t bank_n, uint16_t count) +{ + UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n]; + bank->PCKSIZE.bit.MULTI_PACKET_SIZE = count; +} + +/** \brief Set bank size for IN transactions + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] size Data size for IN. + */ +static inline void _usbd_ep_set_in_size(uint8_t epn, uint8_t bank_n, uint16_t size) +{ + UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n]; + bank->PCKSIZE.bit.BYTE_COUNT = size; +} + +/** \brief Set bank count for OUT transaction + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] count Data count for OUT. + */ +static inline void _usbd_ep_set_out_count(uint8_t epn, uint8_t bank_n, uint16_t count) +{ + UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n]; + bank->PCKSIZE.bit.BYTE_COUNT = count; +} + +/** \brief Set bank size for OUT transactions + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] size Data size for OUT. + */ +static inline void _usbd_ep_set_out_size(uint8_t epn, uint8_t bank_n, uint16_t size) +{ + UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n]; + bank->PCKSIZE.bit.MULTI_PACKET_SIZE = size; +} + +/** Set bank size and count for IN transactions + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] size Data size. + * \param[in] count Initial data count. + */ +static inline void _usbd_ep_set_in_trans(uint8_t epn, uint8_t bank_n, uint32_t size, uint32_t count) +{ + _usbd_ep_set_in_size(epn, bank_n, size); + _usbd_ep_set_in_count(epn, bank_n, count); +} + +/** \brief Set bank size and count for OUT transaction + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] size Data size. + * \param[in] count Initial data count. + */ +static inline void _usbd_ep_set_out_trans(uint8_t epn, uint8_t bank_n, uint32_t size, uint32_t count) +{ + _usbd_ep_set_out_size(epn, bank_n, size); + _usbd_ep_set_out_count(epn, bank_n, count); +} + +/** \brief Clear bank status + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + */ +static inline void _usbd_ep_clear_bank_status(uint8_t epn, uint8_t bank_n) +{ + UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n]; + bank->STATUS_BK.reg = 0; +} + +/** Set IN ready for IN transactions + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] rdy Set to \c true to indicate IN packet ready to TX. + */ +static inline void _usbd_ep_set_in_rdy(uint8_t epn, uint8_t bank_n, const bool rdy) +{ + if (rdy) { + hri_usbendpoint_set_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n); + } else { + hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n); + } +} + +/** \brief Set bank ready for OUT transactions + * \param[in] epn Endpoint number. + * \param[in] bank_n Endpoint bank number. + * \param[in] rdy Set to \c true to indicate OUT bank ready to RX. + */ +static inline void _usbd_ep_set_out_rdy(uint8_t epn, uint8_t bank_n, const bool rdy) +{ + if (rdy) { + hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n); + } else { + hri_usbendpoint_set_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n); + } +} + +/** + * \brief Convert USB endpoint size to HW PCKSIZE.SIZE + * \param[in] n Number of bytes of endpoint size. + */ +static inline uint8_t _usbd_ep_pcksize_size(uint16_t n) +{ + return ( + (n > 512) + ? 7 + : ((n > 256) ? 6 : ((n > 128) ? 5 : ((n > 64) ? 4 : ((n > 32) ? 3 : ((n > 16) ? 2 : ((n > 8) ? 1 : 0))))))); +} + +/** + * \brief Obtain endpoint descriptor pointer + * \param[in] epn Endpoint number. + * \param[in] dir Endpoint direction. + */ +static inline struct _usb_d_dev_ep *_usb_d_dev_ept(uint8_t epn, bool dir) +{ + uint8_t ep_index = (epn == 0) ? 0 : (dir ? (epn + CONF_USB_D_MAX_EP_N) : epn); + return &dev_inst.ep[ep_index]; +} + +/** + * \brief Handles USB SOF interrupt + */ +static inline void _usb_d_dev_sof(void) +{ + /* ACK SOF interrupt. */ + hri_usbdevice_clear_INTFLAG_reg(USB, USB_DEVICE_INTFLAG_SOF); + dev_inst.callbacks.sof(); +} + +/** + * \brief Handles USB LPM Suspend interrupt + */ +static inline void _usb_d_dev_lpmsusp(void) +{ + uint8_t i; + uint32_t lpm_variable = 0; + + /* ACK LPMSUSP interrupt. */ + hri_usbdevice_clear_INTFLAG_reg(USB, USB_D_SUSPEND_INT_FLAGS); + /* Change interrupt masks */ + hri_usbdevice_clear_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS); + hri_usbdevice_set_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS); + + /* Find LPM data */ + for (i = 0; i < CONF_USB_D_MAX_EP_N; i++) { + UsbDeviceDescBank *bank = &prvt_inst.desc_table[i].DeviceDescBank[0]; + if (bank->EXTREG.bit.SUBPID == 0x3) { + /* Save LPM variable */ + lpm_variable = bank->EXTREG.bit.VARIABLE; + /* Clear */ + bank->EXTREG.reg = 0; + break; + } + } + dev_inst.callbacks.event(USB_EV_LPM_SUSPEND, lpm_variable); +} + +/** + * \brief Handles USB RAM Error interrupt + */ +static inline void _usb_d_dev_ramerr(void) +{ + hri_usbdevice_clear_INTFLAG_reg(USB, USB_DEVICE_INTFLAG_RAMACER); + dev_inst.callbacks.event(USB_EV_ERROR, 0); +} + +/** + * \brief Handles USB resume/wakeup interrupts + */ +static inline void _usb_d_dev_wakeup(void) +{ + hri_usbdevice_clear_INTFLAG_reg(USB, USB_D_WAKEUP_INT_FLAGS); + hri_usbdevice_clear_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS); + hri_usbdevice_set_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS); + + _usb_d_dev_wait_clk_rdy(CONF_USB_D_CLK_SRC); + dev_inst.callbacks.event(USB_EV_WAKEUP, 0); +} + +/** + * \brief Handles USB signal reset interrupt + */ +static inline void _usb_d_dev_reset(void) +{ + /* EP0 will not be reseted by USB RESET, disable manually. */ + hri_usbendpoint_write_EPCFG_reg(USB, 0, 0); + + hri_usbdevice_clear_INTFLAG_reg(USB, USB_DEVICE_INTFLAG_EORST); + hri_usbdevice_clear_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS); + hri_usbdevice_set_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS); + + _usb_d_dev_reset_epts(); + dev_inst.callbacks.event(USB_EV_RESET, 0); +} + +static inline void _usb_d_dev_suspend(void) +{ + hri_usbdevice_clear_INTFLAG_reg(USB, USB_D_SUSPEND_INT_FLAGS); + hri_usbdevice_clear_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS); + hri_usbdevice_set_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS); + + dev_inst.callbacks.event(USB_EV_SUSPEND, 0); +} + +/** + * \brief Handles USB non-endpoint interrupt + */ +static inline bool _usb_d_dev_handle_nep(void) +{ + bool rc = true; + uint16_t flags = hri_usbdevice_read_INTFLAG_reg(USB); + flags &= hri_usbdevice_read_INTEN_reg(USB); + + if (flags & USB_DEVICE_INTFLAG_SOF) { + _usb_d_dev_sof(); + return true; + } + if (flags & USB_DEVICE_INTFLAG_LPMSUSP) { + _usb_d_dev_lpmsusp(); + } else if (flags & USB_DEVICE_INTFLAG_RAMACER) { + _usb_d_dev_ramerr(); + } else if (flags & USB_D_WAKEUP_INT_FLAGS) { + _usb_d_dev_wakeup(); + } else if (flags & USB_DEVICE_INTFLAG_EORST) { + _usb_d_dev_reset(); + } else if (flags & USB_DEVICE_INTFLAG_SUSPEND) { + _usb_d_dev_suspend(); + } else { + rc = false; + } + return rc; +} + +/** + * \brief Prepare next IN transactions + * \param[in] ept Pointer to endpoint information. + * \param[in] isr Invoked from ISR. + */ +static void _usb_d_dev_in_next(struct _usb_d_dev_ep *ept, bool isr) +{ + Usb * hw = USB; + uint8_t epn = USB_EP_GET_N(ept->ep); + UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[0]; + uint16_t trans_count = isr ? bank[1].PCKSIZE.bit.BYTE_COUNT : 0; + uint16_t trans_next; + uint16_t last_pkt = trans_count & ((ept->size == 1023) ? ept->size : (ept->size - 1)); + uint8_t inten = 0; + bool is_ctrl = _usb_d_dev_ep_is_ctrl(ept); + + if (isr) { + _usbd_ep_ack_io_cpt(epn, 1); + } + + ept->trans_count += trans_count; + /* Send more data. */ + if (ept->trans_count < ept->trans_size) { + trans_next = ept->trans_size - ept->trans_count; + if (ept->flags.bits.use_cache) { + if (trans_next > ept->size) { + trans_next = ept->size; + } + memcpy(ept->cache, &ept->trans_buf[ept->trans_count], trans_next); + _usbd_ep_set_buf(epn, 1, (uint32_t)ept->cache); + } else { + if (trans_next > USB_D_DEV_TRANS_MAX) { + trans_next = USB_D_DEV_TRANS_MAX; + } + _usbd_ep_set_buf(epn, 1, (uint32_t)&ept->trans_buf[ept->trans_count]); + } + _usbd_ep_set_in_trans(epn, 1, trans_next, 0); + goto _in_tx_exec; + } else if (ept->flags.bits.need_zlp) { + ept->flags.bits.need_zlp = 0; + _usbd_ep_set_in_trans(epn, 1, 0, 0); + goto _in_tx_exec; + } + /* Complete. */ + if (is_ctrl) { + hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK1_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRCPT0); + } else { + hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK1_INT_FLAGS); + } + + /* No ping-pong, so ask more data without background transfer. */ + if (last_pkt == ept->size) { + ept->flags.bits.is_busy = 0; + if (dev_inst.ep_callbacks.more(ept->ep, ept->trans_count)) { + /* More data added. */ + return; + } + ept->flags.bits.is_busy = 1; + } + /* Finish normally. */ + _usb_d_dev_trans_done(ept, USB_TRANS_DONE); + return; + +_in_tx_exec: + if (!isr) { + if (is_ctrl) { + /* Control endpoint: SETUP or OUT will abort IN transaction. + * SETUP: terminate the IN without any notification. Trigger + * SETUP callback. + * OUT NAK: terminate IN. + */ + inten = USB_D_BANK1_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRFAIL0; + } else { + /* Initialize normal IN transaction. */ + inten = USB_D_BANK1_INT_FLAGS; + } + hri_usbendpoint_set_EPINTEN_reg(hw, epn, inten); + } + _usbd_ep_set_in_rdy(epn, 1, true); +} + +/** + * \brief Prepare next OUT transactions + * \param[in] ept Pointer to endpoint information. + * \param[in] isr Invoked from ISR. + */ +static void _usb_d_dev_out_next(struct _usb_d_dev_ep *ept, bool isr) +{ + Usb * hw = USB; + uint8_t epn = USB_EP_GET_N(ept->ep); + UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[0]; + uint16_t trans_size = isr ? bank->PCKSIZE.bit.MULTI_PACKET_SIZE : 0; + uint16_t last_trans = isr ? bank->PCKSIZE.bit.BYTE_COUNT : 0; + uint16_t size_mask = (ept->size == 1023) ? 1023 : (ept->size - 1); + uint16_t last_pkt = last_trans & size_mask; + uint16_t trans_next; + uint8_t inten; + bool is_ctrl = _usb_d_dev_ep_is_ctrl(ept); + + if (isr) { + _usbd_ep_ack_io_cpt(epn, 0); + } + + /* If cache is used, copy data to buffer. */ + if (ept->flags.bits.use_cache && ept->trans_size) { + uint16_t buf_remain = ept->trans_size - ept->trans_count; + memcpy(&ept->trans_buf[ept->trans_count], ept->cache, (buf_remain > last_pkt) ? last_pkt : buf_remain); + } + + /* Force wait ZLP */ + if (ept->trans_size == 0 && ept->flags.bits.need_zlp) { + ept->flags.bits.need_zlp = 0; + ept->flags.bits.use_cache = 1; + _usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache); + _usbd_ep_set_out_trans(epn, 0, ept->size, 0); + goto _out_rx_exec; + } else if (isr && last_pkt < ept->size) { + /* Short packet. */ + ept->flags.bits.need_zlp = 0; + ept->trans_count += last_trans; + } else { + /* Full packets. */ + ept->trans_count += trans_size; + + /* Wait more data */ + if (ept->trans_count < ept->trans_size) { + /* Continue OUT */ + trans_next = ept->trans_size - ept->trans_count; + if (ept->flags.bits.use_cache) { + /* Expect single packet each time. */ + if (trans_next > ept->size) { + trans_next = ept->size; + } + _usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache); + } else { + /* Multiple packets each time. */ + if (trans_next > ept->size) { + if (trans_next > USB_D_DEV_TRANS_MAX) { + trans_next = USB_D_DEV_TRANS_MAX; + } else { + /* Must expect multiple of ep size. */ + trans_next -= trans_next & size_mask; + } + } else if (trans_next < ept->size) { + /* Last un-aligned packet should be cached. */ + ept->flags.bits.use_cache = 1; + } + _usbd_ep_set_buf(epn, 0, (uint32_t)&ept->trans_buf[ept->trans_count]); + } + _usbd_ep_set_out_trans(epn, 0, trans_next, 0); + goto _out_rx_exec; + } + } + /* Finish normally. */ + if (is_ctrl) { + hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK0_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRFAIL1); + } else { + hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK0_INT_FLAGS); + } + /* Use ep0 out cache for next setup packets */ + if (0 == epn) { + _usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache); + } + _usb_d_dev_trans_done(ept, USB_TRANS_DONE); + return; + +_out_rx_exec: + if (!isr) { + if (is_ctrl) { + /* Initialize control OUT transaction. */ + + /* Control transfer: SETUP or IN request will abort the + * OUT transactions. + * SETUP: terminate OUT without any notification. + * Trigger SETUP notification. + * IN NAK: finish OUT normally. Notify data done. + */ + _usbd_ep_clear_bank_status(epn, 1); + /* Detect OUT, SETUP, NAK IN */ + inten = USB_D_BANK0_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRFAIL1; + } else { + /* Initialize normal OUT transaction. */ + inten = USB_D_BANK0_INT_FLAGS; + } + hri_usbendpoint_set_EPINTEN_reg(hw, epn, inten); + } + _usbd_ep_set_out_rdy(epn, 0, true); +} + +/** + * \brief Handles setup received interrupt + * \param[in] ept Pointer to endpoint information. + */ +static void _usb_d_dev_handle_setup(struct _usb_d_dev_ep *ept) +{ + uint8_t epn = USB_EP_GET_N(ept->ep); + bool is_ctrl = _usb_d_dev_ep_is_ctrl(ept); + + if (!is_ctrl) { + /* Should never be here! */ + _usbd_ep_ack_setup(epn); + _usbd_ep_stop_setup(epn); + return; + } + /* Control transfer: + * SETUP transaction will terminate IN/OUT transaction, + * and start new transaction with received SETUP packet. + */ + if (_usb_d_dev_ep_is_busy(ept)) { + ept->flags.bits.is_busy = 0; + + /* Stop transfer on either direction. */ + _usbd_ep_set_in_rdy(epn, 1, false); + _usbd_ep_set_out_rdy(epn, 0, false); + } + ept->flags.bits.is_stalled = 0; + + /* Clear status and notify SETUP */ + _usbd_ep_clear_bank_status(epn, 0); + _usbd_ep_clear_bank_status(epn, 1); + _usbd_ep_int_ack(epn, USB_D_BANK0_INT_FLAGS | USB_D_BANK1_INT_FLAGS); + _usbd_ep_int_dis(epn, USB_D_BANK0_INT_FLAGS | USB_D_BANK1_INT_FLAGS); + /* Invoke callback. */ + dev_inst.ep_callbacks.setup(ept->ep); +} + +/** + * \brief Handles stall sent interrupt + * \param[in] ept Pointer to endpoint information. + * \param[in] bank_n Bank number. + */ +static void _usb_d_dev_handle_stall(struct _usb_d_dev_ep *ept, const uint8_t bank_n) +{ + uint8_t epn = USB_EP_GET_N(ept->ep); + /* Clear interrupt enable. Leave status there for status check. */ + _usbd_ep_int_stall_en(epn, bank_n, false); + _usb_d_dev_trans_done(ept, USB_TRANS_STALL); +} + +/** + * \brief Handles transaction fail interrupt + * \param[in] ept Pointer to endpoint information. + * \param[in] bank_n Bank number. + */ +static void _usb_d_dev_handle_trfail(struct _usb_d_dev_ep *ept, const uint8_t bank_n) +{ + Usb * hw = USB; + uint8_t epn = USB_EP_GET_N(ept->ep); + const uint8_t fail[2] = {USB_DEVICE_EPINTFLAG_TRFAIL0, USB_DEVICE_EPINTFLAG_TRFAIL1}; + UsbDeviceDescBank *bank = prvt_inst.desc_table[epn].DeviceDescBank; + uint8_t eptype + = bank_n ? hri_usbendpoint_read_EPCFG_EPTYPE1_bf(hw, epn) : hri_usbendpoint_read_EPCFG_EPTYPE0_bf(hw, epn); + bool is_ctrl = _usb_d_dev_ep_is_ctrl(ept); + USB_DEVICE_STATUS_BK_Type st; + st.reg = bank[bank_n].STATUS_BK.reg; + + if ((eptype == USB_D_EPTYPE_ISOCH) && st.bit.CRCERR) { + bank[bank_n].STATUS_BK.bit.CRCERR = 0; + hri_usbendpoint_clear_EPINTFLAG_reg(hw, epn, fail[bank_n]); + hri_usbendpoint_clear_EPINTEN_reg(hw, epn, fail[bank_n]); + _usb_d_dev_trans_stop(ept, bank_n, USB_TRANS_ERROR); + } else if (st.bit.ERRORFLOW) { + bank[bank_n].STATUS_BK.bit.ERRORFLOW = 0; + hri_usbendpoint_clear_EPINTFLAG_reg(hw, epn, fail[bank_n]); + hri_usbendpoint_clear_EPINTEN_reg(hw, epn, fail[bank_n]); + /* Abort control transfer. */ + if (is_ctrl && _usb_d_dev_ep_is_busy(ept)) { + if (bank_n != _usb_d_dev_ep_is_in(ept)) { + _usb_d_dev_trans_stop(ept, _usb_d_dev_ep_is_in(ept), USB_TRANS_DONE); + } + } + } else { + _usbd_ep_clear_bank_status(epn, bank_n); + hri_usbendpoint_clear_EPINTFLAG_reg(hw, epn, fail[bank_n]); + hri_usbendpoint_clear_EPINTEN_reg(hw, epn, fail[bank_n]); + } +} + +/** + * \brief Analyze flags for setup transaction + * \param[in] ept Pointer to endpoint information. + * \param[in] flags Endpoint interrupt flags. + */ +static inline void _usb_d_dev_trans_setup_isr(struct _usb_d_dev_ep *ept, const uint8_t flags) +{ + /* + * SETPU is automatically ACKed by hardware + * OUT & IN should be set to NAK when checking SETUP + * No need to check OUT & IN status. + */ + if (flags & USB_DEVICE_EPINTFLAG_RXSTP) { + _usb_d_dev_handle_setup(ept); + } else if (flags & USB_DEVICE_EPINTFLAG_STALL1) { + _usb_d_dev_handle_stall(ept, 1); + } else if (flags & USB_DEVICE_EPINTFLAG_STALL0) { + _usb_d_dev_handle_stall(ept, 0); + } +} + +/** + * \brief Analyze flags for IN transactions + * \param[in] ept Pointer to endpoint information. + * \param[in] flags Endpoint interrupt flags. + */ +static inline void _usb_d_dev_trans_in_isr(struct _usb_d_dev_ep *ept, const uint8_t flags) +{ + /* + * Check IN flags + * If control endpoint, SETUP & OUT is checked to see if abort + */ + if (flags & USB_DEVICE_EPINTFLAG_STALL1) { + _usb_d_dev_handle_stall(ept, 1); + } else if (flags & USB_DEVICE_EPINTFLAG_TRFAIL1) { + _usb_d_dev_handle_trfail(ept, 1); + } else if (flags & USB_DEVICE_EPINTFLAG_TRCPT1) { + _usb_d_dev_in_next(ept, true); + } else if (_usb_d_dev_ep_is_ctrl(ept)) { + /* Check OUT NAK + * Check SETUP + */ + if (flags & USB_DEVICE_EPINTFLAG_TRFAIL0) { + _usb_d_dev_handle_trfail(ept, 0); + } else if (flags & USB_DEVICE_EPINTFLAG_RXSTP) { + _usb_d_dev_handle_setup(ept); + } + } +} + +/** + * \brief Analyze flags for OUT transactions + * \param[in] ept Pointer to endpoint information. + * \param[in] flags Endpoint interrupt flags. + */ +static inline void _usb_d_dev_trans_out_isr(struct _usb_d_dev_ep *ept, const uint8_t flags) +{ + /* + * Check OUT flags. + * If control endpoint, SETUP & IN NAK is checked to see if abort + */ + if (flags & USB_DEVICE_EPINTFLAG_STALL0) { + _usb_d_dev_handle_stall(ept, 0); + } else if (flags & USB_DEVICE_EPINTFLAG_TRFAIL0) { + _usb_d_dev_handle_trfail(ept, 0); + } else if (flags & USB_DEVICE_EPINTFLAG_TRCPT0) { + _usb_d_dev_out_next(ept, true); + } else if (_usb_d_dev_ep_is_ctrl(ept)) { + /* Check IN NAK + * Check SETUP + */ + if (flags & USB_DEVICE_EPINTFLAG_TRFAIL1) { + _usb_d_dev_handle_trfail(ept, 1); + } else if (flags & USB_DEVICE_EPINTFLAG_RXSTP) { + _usb_d_dev_handle_setup(ept); + } + } +} + +/** + * \brief Handles the endpoint interrupts. + * \param[in] epint Endpoint interrupt summary (by bits). + * \param[in] ept Pointer to endpoint information. + */ +static inline void _usb_d_dev_handle_eps(uint32_t epint, struct _usb_d_dev_ep *ept) +{ + Usb *hw = USB; + + uint8_t flags, mask; + uint8_t epn = USB_EP_GET_N(ept->ep); + + if (!(epint & (1u << epn))) { + return; + } + flags = hw->DEVICE.DeviceEndpoint[epn].EPINTFLAG.reg; + mask = hw->DEVICE.DeviceEndpoint[epn].EPINTENSET.reg; + flags &= mask; + if (flags) { + if (!_usb_d_dev_ep_is_busy(ept)) { + _usb_d_dev_trans_setup_isr(ept, flags); + } else if (_usb_d_dev_ep_is_in(ept)) { + _usb_d_dev_trans_in_isr(ept, flags); + } else { + _usb_d_dev_trans_out_isr(ept, flags); + } + } +} + +/** + * \brief USB device interrupt handler + * \param[in] unused The parameter is not used + */ +static void _usb_d_dev_handler(void) +{ + Usb * hw = USB; + uint8_t i; + + uint16_t epint = hw->DEVICE.EPINTSMRY.reg; + if (0 == epint) { + if (_usb_d_dev_handle_nep()) { + return; + } + } + /* Handle endpoints */ + for (i = 0; i < USB_D_N_EP; i++) { + struct _usb_d_dev_ep *ept = &dev_inst.ep[i]; + if (ept->ep == 0xFF) { + continue; + } + _usb_d_dev_handle_eps(epint, ept); + } +} + +/** + * \brief Reset all endpoint software instances + */ +static void _usb_d_dev_reset_epts(void) +{ + uint8_t i; + for (i = 0; i < USB_D_N_EP; i++) { + _usb_d_dev_trans_done(&dev_inst.ep[i], USB_TRANS_RESET); + dev_inst.ep[i].ep = 0xFF; + dev_inst.ep[i].flags.u8 = 0; + } + memset(prvt_inst.desc_table, 0, sizeof(UsbDeviceDescriptor) * (CONF_USB_D_MAX_EP_N + 1)); +} + +int32_t _usb_d_dev_init(void) +{ + Usb * hw = USB; + uint8_t speed = CONF_USB_D_SPEED; + const uint8_t spdconf[4] = { + USB_DEVICE_CTRLB_SPDCONF(1), /* LS */ + USB_DEVICE_CTRLB_SPDCONF(0), /* FS */ + 0, + 0 /* Reserved */ + }; + + hri_usbdevice_wait_for_sync(hw, USB_SYNCBUSY_SWRST); + if (hri_usbdevice_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + hri_usbdevice_set_CTRLA_SWRST_bit(hw); + hri_usbdevice_wait_for_sync(hw, USB_SYNCBUSY_SWRST); + + dev_inst.callbacks.sof = (_usb_d_dev_sof_cb_t)_dummy_func_no_return; + dev_inst.callbacks.event = (_usb_d_dev_event_cb_t)_dummy_func_no_return; + + dev_inst.ep_callbacks.setup = (_usb_d_dev_ep_cb_setup_t)_dummy_func_no_return; + dev_inst.ep_callbacks.more = (_usb_d_dev_ep_cb_more_t)_dummy_func_no_return; + dev_inst.ep_callbacks.done = (_usb_d_dev_ep_cb_done_t)_dummy_func_no_return; + + _usb_d_dev_reset_epts(); + + _usb_load_calib(); + + hri_usbdevice_write_CTRLA_reg(hw, USB_CTRLA_RUNSTDBY); + hri_usbdevice_write_DESCADD_reg(hw, (uint32_t)prvt_inst.desc_table); + hri_usbdevice_write_CTRLB_reg(hw, spdconf[speed] | USB_DEVICE_CTRLB_DETACH); + + return ERR_NONE; +} + +void _usb_d_dev_deinit(void) +{ + Usb *hw = USB; + + while (_usb_d_dev_disable() < 0) + ; + + hri_usbdevice_write_CTRLA_reg(hw, USB_CTRLA_SWRST); + + NVIC_DisableIRQ(USB_0_IRQn); + NVIC_ClearPendingIRQ(USB_0_IRQn); + NVIC_DisableIRQ(USB_1_IRQn); + NVIC_ClearPendingIRQ(USB_1_IRQn); + NVIC_DisableIRQ(USB_2_IRQn); + NVIC_ClearPendingIRQ(USB_2_IRQn); + NVIC_DisableIRQ(USB_3_IRQn); + NVIC_ClearPendingIRQ(USB_3_IRQn); +} + +int32_t _usb_d_dev_enable(void) +{ + Usb * hw = USB; + uint8_t ctrla; + + if (hri_usbdevice_get_SYNCBUSY_reg(hw, (USB_SYNCBUSY_ENABLE | USB_SYNCBUSY_SWRST))) { + return -USB_ERR_DENIED; + } + ctrla = hri_usbdevice_read_CTRLA_reg(hw); + if ((ctrla & USB_CTRLA_ENABLE) == 0) { + hri_usbdevice_write_CTRLA_reg(hw, ctrla | USB_CTRLA_ENABLE); + } + + NVIC_EnableIRQ(USB_0_IRQn); + NVIC_EnableIRQ(USB_1_IRQn); + NVIC_EnableIRQ(USB_2_IRQn); + NVIC_EnableIRQ(USB_3_IRQn); + + hri_usbdevice_set_INTEN_reg(hw, + USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST | USB_DEVICE_INTENSET_RAMACER + | USB_D_SUSPEND_INT_FLAGS); + + return ERR_NONE; +} + +int32_t _usb_d_dev_disable(void) +{ + Usb * hw = USB; + uint8_t ctrla; + + if (hri_usbdevice_get_SYNCBUSY_reg(hw, (USB_SYNCBUSY_ENABLE | USB_SYNCBUSY_SWRST))) { + return -USB_ERR_DENIED; + } + + ctrla = hri_usbdevice_read_CTRLA_reg(hw); + if (ctrla & USB_CTRLA_ENABLE) { + hri_usbdevice_write_CTRLA_reg(hw, ctrla & ~USB_CTRLA_ENABLE); + } + + NVIC_DisableIRQ(USB_0_IRQn); + NVIC_DisableIRQ(USB_1_IRQn); + NVIC_DisableIRQ(USB_2_IRQn); + NVIC_DisableIRQ(USB_3_IRQn); + + hri_usbdevice_clear_INTEN_reg(hw, + USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST | USB_DEVICE_INTENSET_RAMACER + | USB_D_SUSPEND_INT_FLAGS + | USB_D_WAKEUP_INT_FLAGS); + + return ERR_NONE; +} + +void _usb_d_dev_attach(void) +{ + hri_usbdevice_clear_CTRLB_DETACH_bit(USB); +} + +void _usb_d_dev_detach(void) +{ + hri_usbdevice_set_CTRLB_DETACH_bit(USB); +} + +#ifndef USB_FSMSTATUS_FSMSTATE_ON +#define USB_FSMSTATUS_FSMSTATE_ON USB_FSMSTATUS_FSMSTATE(2ul) +#endif +void _usb_d_dev_send_remotewakeup(void) +{ + uint32_t retry = CONF_USB_RMT_WKUP_RETRY; + _usb_d_dev_wait_clk_rdy(CONF_USB_D_CLK_SRC); + while ((USB_FSMSTATUS_FSMSTATE_ON != hri_usbdevice_read_FSMSTATUS_FSMSTATE_bf(USB)) && (retry--)) { + USB->DEVICE.CTRLB.bit.UPRSM = 1; + } +} + +enum usb_speed _usb_d_dev_get_speed(void) +{ + uint8_t sp = (enum usb_speed)hri_usbdevice_read_STATUS_SPEED_bf(USB); + const enum usb_speed speed[2] = {USB_SPEED_FS, USB_SPEED_LS}; + + return speed[sp]; +} + +void _usb_d_dev_set_address(uint8_t addr) +{ + hri_usbdevice_write_DADD_reg(USB, USB_DEVICE_DADD_ADDEN | USB_DEVICE_DADD_DADD(addr)); +} + +uint8_t _usb_d_dev_get_address(void) +{ + uint8_t addr = hri_usbdevice_read_DADD_DADD_bf(USB); + return addr; +} + +uint16_t _usb_d_dev_get_frame_n(void) +{ + uint16_t fn = hri_usbdevice_read_FNUM_FNUM_bf(USB); + return fn; +} + +uint8_t _usb_d_dev_get_uframe_n(void) +{ + uint8_t ufn = hri_usbdevice_read_FNUM_MFNUM_bf(USB); + return ufn; +} + +/** + * \brief Start a setup transaction + * \param[in] ept Endpoint information. + */ +static inline void _usb_d_dev_trans_setup(struct _usb_d_dev_ep *ept) +{ + Usb * hw = USB; + uint8_t epn = USB_EP_GET_N(ept->ep); + + _usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache); + _usbd_ep_set_out_trans(epn, 0, ept->size, 0); + + hri_usbendpoint_clear_EPSTATUS_reg(hw, epn, USB_DEVICE_EPSTATUS_STALLRQ(0x3) | USB_DEVICE_EPSTATUS_BK1RDY); + _usbd_ep_set_out_rdy(epn, 0, false); + + hri_usbendpoint_set_EPINTEN_reg(hw, epn, USB_D_SETUP_INT_FLAGS); +} + +int32_t _usb_d_dev_ep0_init(const uint8_t max_pkt_siz) +{ + return _usb_d_dev_ep_init(0, USB_EP_XTYPE_CTRL, max_pkt_siz); +} + +int32_t _usb_d_dev_ep_init(const uint8_t ep, const uint8_t attr, const uint16_t max_pkt_siz) +{ + uint8_t epn = USB_EP_GET_N(ep); + bool dir = USB_EP_GET_DIR(ep); + struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir); + + uint8_t ep_type = attr & USB_EP_XTYPE_MASK; + const struct _usb_ep_cfg_item *pcfg = &_usb_ep_cfgs[epn]; + + if (epn > CONF_USB_D_MAX_EP_N) { + return -USB_ERR_PARAM; + } + if (ept->ep != 0xFF) { + return -USB_ERR_REDO; + } + if (ep_type == USB_EP_XTYPE_CTRL) { + struct _usb_d_dev_ep *ept_in = _usb_d_dev_ept(epn, !dir); + if (ept_in->ep != 0xFF) { + return -USB_ERR_REDO; + } + if (pcfg->cache == NULL) { + return -USB_ERR_FUNC; + } + } + if ((dir ? pcfg->i_cache : pcfg->cache) && ((dir ? pcfg->i_size : pcfg->size) < max_pkt_siz)) { + return -USB_ERR_FUNC; + } + + /* Initialize EP n settings */ + ept->cache = (uint8_t *)(dir ? pcfg->i_cache : pcfg->cache); + ept->size = max_pkt_siz; + ept->flags.u8 = (ep_type + 1); + ept->ep = ep; + + return USB_OK; +} + +void _usb_d_dev_ep_deinit(uint8_t ep) +{ + Usb * hw = USB; + uint8_t epn = USB_EP_GET_N(ep); + bool dir = USB_EP_GET_DIR(ep); + struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir); + + if (epn > CONF_USB_D_MAX_EP_N || !_usb_d_dev_ep_is_used(ept)) { + return; + } + + /* Finish pending transactions. */ + _usb_d_dev_trans_stop(ept, dir, USB_TRANS_RESET); + + /* Disable the endpoint. */ + if (_usb_d_dev_ep_is_ctrl(ept)) { + hw->DEVICE.DeviceEndpoint[ep].EPCFG.reg = 0; + } else if (USB_EP_GET_DIR(ep)) { + hw->DEVICE.DeviceEndpoint[USB_EP_GET_N(ep)].EPCFG.reg &= ~USB_DEVICE_EPCFG_EPTYPE1_Msk; + } else { + hw->DEVICE.DeviceEndpoint[ep].EPCFG.reg &= ~USB_DEVICE_EPCFG_EPTYPE0_Msk; + } + ept->flags.u8 = 0; + ept->ep = 0xFF; +} + +int32_t _usb_d_dev_ep_enable(const uint8_t ep) +{ + Usb * hw = USB; + uint8_t epn = USB_EP_GET_N(ep); + bool dir = USB_EP_GET_DIR(ep); + struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir); + uint8_t epcfg = hri_usbendpoint_read_EPCFG_reg(hw, epn); + UsbDeviceDescBank * bank; + + if (epn > CONF_USB_D_MAX_EP_N || !_usb_d_dev_ep_is_used(ept)) { + return -USB_ERR_PARAM; + } + + bank = prvt_inst.desc_table[epn].DeviceDescBank; + if (ept->flags.bits.eptype == USB_D_EPTYPE_CTRL) { + if (epcfg & (USB_DEVICE_EPCFG_EPTYPE1_Msk | USB_DEVICE_EPCFG_EPTYPE0_Msk)) { + return -USB_ERR_REDO; + } + hri_usbendpoint_write_EPCFG_reg(hw, epn, USB_D_EPCFG_CTRL); + bank[0].PCKSIZE.reg = USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE(ept->size) + | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size)); + bank[1].PCKSIZE.reg + = USB_DEVICE_PCKSIZE_BYTE_COUNT(ept->size) | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size)); + /* By default, control endpoint accept SETUP and NAK all other token. */ + _usbd_ep_set_out_rdy(epn, 0, false); + _usbd_ep_set_in_rdy(epn, 1, false); + + _usbd_ep_clear_bank_status(epn, 0); + _usbd_ep_clear_bank_status(epn, 1); + + /* Enable SETUP reception for control endpoint. */ + _usb_d_dev_trans_setup(ept); + + } else if (dir) { + if (epcfg & USB_DEVICE_EPCFG_EPTYPE1_Msk) { + return -USB_ERR_REDO; + } + epcfg |= USB_DEVICE_EPCFG_EPTYPE1(ept->flags.bits.eptype); + hri_usbendpoint_write_EPCFG_reg(hw, epn, epcfg); + + bank[1].PCKSIZE.reg + = USB_DEVICE_PCKSIZE_BYTE_COUNT(ept->size) | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size)); + + /* By default, IN endpoint will NAK all token. */ + _usbd_ep_set_in_rdy(epn, 1, false); + _usbd_ep_clear_bank_status(epn, 1); + + } else { + + if (epcfg & USB_DEVICE_EPCFG_EPTYPE0_Msk) { + return -USB_ERR_REDO; + } + epcfg |= USB_DEVICE_EPCFG_EPTYPE0(ept->flags.bits.eptype); + hri_usbendpoint_write_EPCFG_reg(hw, epn, epcfg); + + bank[0].PCKSIZE.reg = USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE(ept->size) + | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size)); + + /* By default, OUT endpoint will NAK all token. */ + _usbd_ep_set_out_rdy(epn, 0, false); + _usbd_ep_clear_bank_status(epn, 0); + } + + return USB_OK; +} + +void _usb_d_dev_ep_disable(const uint8_t ep) +{ + Usb * hw = USB; + uint8_t epn = USB_EP_GET_N(ep); + bool dir = USB_EP_GET_DIR(ep); + struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir); + + _usb_d_dev_trans_stop(ept, dir, USB_TRANS_RESET); + if (_usb_d_dev_ep_is_ctrl(ept)) { + hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_ALL_INT_FLAGS); + } +} + +/** + * \brief Get endpoint stall status + * \param[in] ept Pointer to endpoint information. + * \param[in] dir Endpoint direction. + * \return Stall status. + * \retval \c true Endpoint is stalled. + * \retval \c false Endpoint is not stalled. + */ +static inline int32_t _usb_d_dev_ep_stall_get(struct _usb_d_dev_ep *ept, bool dir) +{ + uint8_t epn = USB_EP_GET_N(ept->ep); + return _usbd_ep_is_stalled(epn, dir); +} + +/** + * \brief Set endpoint stall + * \param[in, out] ept Pointer to endpoint information. + * \param[in] dir Endpoint direction. + * \return Always 0, success. + */ +static inline int32_t _usb_d_dev_ep_stall_set(struct _usb_d_dev_ep *ept, bool dir) +{ + uint8_t epn = USB_EP_GET_N(ept->ep); + _usbd_ep_set_stall(epn, dir, true); + _usbd_ep_int_en(epn, USB_DEVICE_EPINTFLAG_STALL0 << dir); + ept->flags.bits.is_stalled = 1; + /* In stall interrupt abort the transfer. */ + return ERR_NONE; +} + +/** + * \brief Clear endpoint stall + * \param[in, out] ept Pointer to endpoint information. + * \param[in] dir Endpoint direction. + * \return Always 0, success. + */ +static inline int32_t _usb_d_dev_ep_stall_clr(struct _usb_d_dev_ep *ept, bool dir) +{ + uint8_t epn = USB_EP_GET_N(ept->ep); + bool is_stalled = _usbd_ep_is_stalled(epn, dir); + if (!is_stalled) { + return ERR_NONE; + } + _usbd_ep_set_stall(epn, dir, false); + _usbd_ep_int_dis(epn, USB_DEVICE_EPINTFLAG_STALL0 << dir); + if (_usbd_ep_is_stall_sent(epn, dir)) { + _usbd_ep_ack_stall(epn, dir); + _usbd_ep_set_toggle(epn, dir, 0); + } + if (_usb_d_dev_ep_is_ctrl(ept)) { + if ((hri_usbendpoint_read_EPSTATUS_reg(USB, epn) & USB_DEVICE_EPSTATUS_STALLRQ_Msk) == 0) { + ept->flags.bits.is_stalled = 0; + } + } else { + ept->flags.bits.is_stalled = 0; + } + return ERR_NONE; +} + +int32_t _usb_d_dev_ep_stall(const uint8_t ep, const enum usb_ep_stall_ctrl ctrl) +{ + uint8_t epn = USB_EP_GET_N(ep); + bool dir = USB_EP_GET_DIR(ep); + struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir); + int32_t rc; + + if (epn > CONF_USB_D_MAX_EP_N) { + return -USB_ERR_PARAM; + } + + if (USB_EP_STALL_SET == ctrl) { + rc = _usb_d_dev_ep_stall_set(ept, dir); + } else if (USB_EP_STALL_CLR == ctrl) { + rc = _usb_d_dev_ep_stall_clr(ept, dir); + } else { + rc = _usb_d_dev_ep_stall_get(ept, dir); + } + return rc; +} + +/** + * \brief Finish the transaction and invoke callback + * \param[in, out] ept Pointer to endpoint information. + * \param[in] code Information code passed. + */ +static void _usb_d_dev_trans_done(struct _usb_d_dev_ep *ept, const int32_t code) +{ + if (!(_usb_d_dev_ep_is_used(ept) && _usb_d_dev_ep_is_busy(ept))) { + return; + } + ept->flags.bits.is_busy = 0; + dev_inst.ep_callbacks.done(ept->ep, code, ept->trans_count); +} + +/** + * \brief Terminate the transaction with specific status code + * \param[in, out] ept Pointer to endpoint information. + * \param[in] dir Endpoint direction. + * \param[in] code Information code passed. + */ +static void _usb_d_dev_trans_stop(struct _usb_d_dev_ep *ept, bool dir, const int32_t code) +{ + uint8_t epn = USB_EP_GET_N(ept->ep); + ; + const uint8_t intflags[2] = {USB_D_BANK0_INT_FLAGS, USB_D_BANK1_INT_FLAGS}; + if (!(_usb_d_dev_ep_is_used(ept) && _usb_d_dev_ep_is_busy(ept))) { + return; + } + /* Stop transfer */ + if (dir) { + /* NAK IN */ + _usbd_ep_set_in_rdy(epn, 1, false); + } else { + /* NAK OUT */ + _usbd_ep_set_out_rdy(epn, 0, false); + } + _usbd_ep_int_ack(epn, intflags[dir]); + _usbd_ep_int_dis(epn, intflags[dir]); + _usb_d_dev_trans_done(ept, code); +} + +int32_t _usb_d_dev_ep_read_req(const uint8_t ep, uint8_t *req_buf) +{ + uint8_t epn = USB_EP_GET_N(ep); + UsbDeviceDescBank *bank = prvt_inst.desc_table[epn].DeviceDescBank; + uint32_t addr = bank[0].ADDR.reg; + uint16_t bytes = bank[0].PCKSIZE.bit.BYTE_COUNT; + + if (epn > CONF_USB_D_MAX_EP_N || !req_buf) { + return -USB_ERR_PARAM; + } + if (!_usbd_ep_is_ctrl(epn)) { + return -USB_ERR_FUNC; + } + if (!_usbd_ep_is_setup(epn)) { + return ERR_NONE; + } + memcpy(req_buf, (void *)addr, 8); + _usbd_ep_ack_setup(epn); + + return bytes; +} + +int32_t _usb_d_dev_ep_trans(const struct usb_d_transfer *trans) +{ + uint8_t epn = USB_EP_GET_N(trans->ep); + bool dir = USB_EP_GET_DIR(trans->ep); + struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir); + + uint16_t size_mask = (ept->size == 1023) ? 1023 : (ept->size - 1); + bool size_n_aligned = (trans->size & size_mask); + + bool use_cache = false; + + volatile hal_atomic_t flags; + + if (epn > CONF_USB_D_MAX_EP_N) { + return -USB_ERR_PARAM; + } + + /* Cases that needs cache: + * 1. Buffer not in RAM (cache all). + * 2. IN/OUT with unaligned buffer (cache all). + * 3. OUT with unaligned packet size (cache last packet). + * 4. OUT size < 8 (sub-case for 3). + */ + if (!_usb_is_addr4dma(trans->buf, trans->size) || (!_usb_is_aligned(trans->buf)) + || (!dir && (trans->size < ept->size))) { + if (!ept->cache) { + return -33; + return -USB_ERR_FUNC; + } + /* Use cache all the time. */ + use_cache = true; + } + if (!dir && size_n_aligned) { + if (!ept->cache) { + return -USB_ERR_PARAM; + } + /* Set 'use_cache' on last packet. */ + } + + /* Check halt */ + if (ept->flags.bits.is_stalled) { + return USB_HALTED; + } + + /* Try to start transactions. */ + + atomic_enter_critical(&flags); + if (_usb_d_dev_ep_is_busy(ept)) { + atomic_leave_critical(&flags); + return USB_BUSY; + } + ept->flags.bits.is_busy = 1; + atomic_leave_critical(&flags); + + /* Copy transaction information. */ + ept->trans_buf = trans->buf; + ept->trans_size = trans->size; + ept->trans_count = 0; + + ept->flags.bits.dir = dir; + ept->flags.bits.use_cache = use_cache; + ept->flags.bits.need_zlp = (trans->zlp && (!size_n_aligned)); + + if (dir) { + _usb_d_dev_in_next(ept, false); + } else { + _usb_d_dev_out_next(ept, false); + } + + return ERR_NONE; +} + +void _usb_d_dev_ep_abort(const uint8_t ep) +{ + uint8_t epn = USB_EP_GET_N(ep); + bool dir = USB_EP_GET_DIR(ep); + struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir); + if (epn > CONF_USB_D_MAX_EP_N) { + return; + } + _usb_d_dev_trans_stop(ept, dir, USB_TRANS_ABORT); +} + +int32_t _usb_d_dev_ep_get_status(const uint8_t ep, struct usb_d_trans_status *stat) +{ + uint8_t epn = USB_EP_GET_N(ep); + bool dir = USB_EP_GET_DIR(ep); + struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir); + bool busy, stall; + + if (epn > CONF_USB_D_MAX_EP_N) { + return USB_ERR_PARAM; + } + busy = ept->flags.bits.is_busy; + stall = ept->flags.bits.is_stalled; + if (stat) { + stat->stall = stall; + stat->busy = busy; + stat->setup = USB->DEVICE.DeviceEndpoint[epn].EPINTFLAG.bit.RXSTP; + stat->dir = ept->flags.bits.dir; + stat->size = ept->trans_size; + stat->count = ept->trans_count; + stat->ep = ep; + stat->xtype = ept->flags.bits.eptype - 1; + } + if (stall) { + return USB_HALTED; + } + if (busy) { + return USB_BUSY; + } + return USB_OK; +} + +void _usb_d_dev_register_callback(const enum usb_d_cb_type type, const FUNC_PTR func) +{ + FUNC_PTR f = (func == NULL) ? (FUNC_PTR)_dummy_func_no_return : (FUNC_PTR)func; + if (type == USB_D_CB_EVENT) { + dev_inst.callbacks.event = (_usb_d_dev_event_cb_t)f; + } else if (type == USB_D_CB_SOF) { + dev_inst.callbacks.sof = (_usb_d_dev_sof_cb_t)f; + } +} + +void _usb_d_dev_register_ep_callback(const enum usb_d_dev_ep_cb_type type, const FUNC_PTR func) +{ + FUNC_PTR f = (func == NULL) ? (FUNC_PTR)_dummy_func_no_return : (FUNC_PTR)func; + if (type == USB_D_DEV_EP_CB_SETUP) { + dev_inst.ep_callbacks.setup = (_usb_d_dev_ep_cb_setup_t)f; + } else if (type == USB_D_DEV_EP_CB_MORE) { + dev_inst.ep_callbacks.more = (_usb_d_dev_ep_cb_more_t)f; + } else if (type == USB_D_DEV_EP_CB_DONE) { + dev_inst.ep_callbacks.done = (_usb_d_dev_ep_cb_done_t)f; + } +} + +/** +* \brief USB interrupt handler +*/ +void USB_0_Handler(void) +{ + + _usb_d_dev_handler(); +} +/** +* \brief USB interrupt handler +*/ +void USB_1_Handler(void) +{ + + _usb_d_dev_handler(); +} +/** +* \brief USB interrupt handler +*/ +void USB_2_Handler(void) +{ + + _usb_d_dev_handler(); +} +/** +* \brief USB interrupt handler +*/ +void USB_3_Handler(void) +{ + + _usb_d_dev_handler(); +} |
