| |
| /** |
| * \file |
| * |
| * \brief Generic DMAC related functionality. |
| * |
| * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. |
| * |
| * \asf_license_start |
| * |
| * \page License |
| * |
| * Subject to your compliance with these terms, you may use Microchip |
| * software and any derivatives exclusively with Microchip products. |
| * It is your responsibility to comply with third party license terms applicable |
| * to your use of third party software (including open source software) that |
| * may accompany Microchip software. |
| * |
| * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, |
| * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, |
| * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, |
| * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE |
| * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL |
| * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE |
| * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE |
| * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT |
| * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY |
| * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, |
| * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. |
| * |
| * \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) |
| DmacDescriptor _descriptor_section[DMAC_CH_NUM]; |
| |
| /* Section containing current descriptors for all DMAC channels */ |
| COMPILER_ALIGNED(16) |
| 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_set_next_descriptor(const uint8_t current_channel, const uint8_t next_channel) |
| { |
| hri_dmacdescriptor_write_DESCADDR_reg(&_descriptor_section[current_channel], |
| (uint32_t)&_descriptor_section[next_channel]); |
| |
| 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; |
| } |
| |
| int32_t _dma_dstinc_enable(const uint8_t channel, const bool enable) |
| { |
| hri_dmacdescriptor_write_BTCTRL_DSTINC_bit(&_descriptor_section[channel], enable); |
| |
| 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 */ |