/* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ #ifndef __GPDMA_H__ #define __GPDMA_H__ #include #include #include #include "hal.h" #include "utility.hpp" namespace lpc43xx { namespace gpdma { /* LPC43xx DMA appears to be the ARM PrimeCell(R) DMA Controller, or very * closely related. * More here: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0196g/Chdcdaeb.html */ constexpr size_t buffer_words(const size_t bytes, const size_t word_size) { return (bytes + word_size - 1) / word_size; } using TCHandler = void (*)(void); using ErrHandler = void (*)(void); enum class FlowControl { MemoryToMemory_DMAControl = 0x0, MemoryToPeripheral_DMAControl = 0x1, PeripheralToMemory_DMAControl = 0x2, SourcePeripheralToDestinationPeripheral_DMAControl = 0x3, SourcePeripheralToDestinationPeripheral_DestinationControl = 0x4, MemoryToPeripheral_PeripheralControl = 0x5, PeripheralToMemory_PeripheralControl = 0x6, SourcePeripheralToDestinationPeripheral_SourceControl = 0x7, }; static const uint_fast8_t flow_control_peripheral_source_map = 0b11011100; constexpr uint_fast8_t source_endpoint_type(const FlowControl flow_control) { return (flow_control_peripheral_source_map >> toUType(flow_control)) & 1; } static const uint_fast8_t flow_control_peripheral_destination_map = 0b11111010; constexpr uint_fast8_t destination_endpoint_type(const FlowControl flow_control) { return (flow_control_peripheral_destination_map >> toUType(flow_control)) & 1; } namespace mux { enum class Peripheral0 { SPIFI = 0, SCT_CTOUT_2 = 1, SGPIO14 = 2, TIMER3_MATCH_1 = 3, }; enum class Peripheral1 { TIMER0_MATCH_0 = 0, USART0_TX = 1, }; enum class Peripheral2 { TIMER0_MATCH_1 = 0, USART0_RX = 1, }; enum class Peripheral3 { TIMER1_MATCH_0 = 0, UART1_TX = 1, I2S1_DMAREQ_1 = 2, SSP1_TX = 3, }; enum class Peripheral4 { TIMER1_MATCH_1 = 0, UART1_RX = 1, I2S1_DMAREQ_2 = 2, SSP1_RX = 3, }; enum class Peripheral5 { TIMER2_MATCH_0 = 0, USART2_TX = 1, SSP1_TX = 2, SGPIO15 = 3, }; enum class Peripheral6 { TIMER2_MATCH_1 = 0, USART2_RX = 1, SSP1_RX = 2, SGPIO14 = 3, }; enum class Peripheral7 { TIMER3_MATCH_0 = 0, USART3_TX = 1, SCT_DMAREQ_0 = 2, ADCHS_WRITE = 3, }; enum class Peripheral8 { TIMER3_MATCH_1 = 0, USART3_RX = 1, SCT_DMAREQ_1 = 2, ADCHS_READ = 3, }; enum class Peripheral9 { SSP0_RX = 0, I2S0_DMAREQ_1 = 1, SCT_DMAREQ_1 = 2, }; enum class Peripheral10 { SSP0_TX = 0, I2S0_DMAREQ_2 = 1, SCT_DMAREQ_0 = 2, }; enum class Peripheral11 { SSP1_RX = 0, SGPIO14 = 1, USART0_TX = 2, }; enum class Peripheral12 { SSP1_TX = 0, SGPIO15 = 1, USART0_RX = 2, }; enum class Peripheral13 { ADC0 = 0, SSP1_RX = 2, USART3_RX = 3, }; enum class Peripheral14 { ADC1 = 0, SSP1_TX = 2, USART3_TX = 3, }; enum class Peripheral15 { DAC = 0, SCT_CTOUT_3 = 1, SGPIO15 = 2, TIMER3_MATCH_0 = 3, }; struct MUX { Peripheral0 peripheral_0; Peripheral1 peripheral_1; Peripheral2 peripheral_2; Peripheral3 peripheral_3; Peripheral4 peripheral_4; Peripheral5 peripheral_5; Peripheral6 peripheral_6; Peripheral7 peripheral_7; Peripheral8 peripheral_8; Peripheral9 peripheral_9; Peripheral10 peripheral_10; Peripheral11 peripheral_11; Peripheral12 peripheral_12; Peripheral13 peripheral_13; Peripheral14 peripheral_14; Peripheral15 peripheral_15; constexpr operator uint32_t() const { return (toUType(peripheral_0) << 0) | (toUType(peripheral_1) << 2) | (toUType(peripheral_2) << 4) | (toUType(peripheral_3) << 6) | (toUType(peripheral_4) << 8) | (toUType(peripheral_5) << 10) | (toUType(peripheral_6) << 12) | (toUType(peripheral_7) << 14) | (toUType(peripheral_8) << 16) | (toUType(peripheral_9) << 18) | (toUType(peripheral_10) << 20) | (toUType(peripheral_11) << 22) | (toUType(peripheral_12) << 24) | (toUType(peripheral_13) << 26) | (toUType(peripheral_14) << 28) | (toUType(peripheral_15) << 30); } }; } /* namespace mux */ namespace channel { struct LLI { uint32_t srcaddr; uint32_t destaddr; uint32_t lli; uint32_t control; }; struct LLIPointer { uint32_t lm; uint32_t r; uint32_t lli; constexpr operator uint32_t() const { return ((lm & 1) << 0) | ((r & 1) << 1) | (lli & 0xfffffffc); } }; struct Control { uint32_t transfersize; uint32_t sbsize; uint32_t dbsize; uint32_t swidth; uint32_t dwidth; uint32_t s; uint32_t d; uint32_t si; uint32_t di; uint32_t prot1; uint32_t prot2; uint32_t prot3; uint32_t i; constexpr operator uint32_t() const { return ((transfersize & 0xfff) << 0) | ((sbsize & 7) << 12) | ((dbsize & 7) << 15) | ((swidth & 7) << 18) | ((dwidth & 7) << 21) | ((s & 1) << 24) | ((d & 1) << 25) | ((si & 1) << 26) | ((di & 1) << 27) | ((prot1 & 1) << 28) | ((prot2 & 1) << 29) | ((prot3 & 1) << 30) | ((i & 1) << 31); } }; struct Config { uint32_t e; uint32_t srcperipheral; uint32_t destperipheral; FlowControl flowcntrl; uint32_t ie; uint32_t itc; uint32_t l; uint32_t a; uint32_t h; constexpr operator uint32_t() const { return ((e & 1) << 0) | ((srcperipheral & 0x1f) << 1) | ((destperipheral & 0x1f) << 6) | ((toUType(flowcntrl) & 7) << 11) | ((ie & 1) << 14) | ((itc & 1) << 15) | ((l & 1) << 16) | ((a & 1) << 17) | ((h & 1) << 18); } }; class Channel { public: constexpr Channel( const size_t number) : number(number) { } void enable() const { LPC_GPDMA->CH[number].CONFIG |= (1U << 0); } bool is_enabled() const { return LPC_GPDMA->CH[number].CONFIG & (1U << 0); } void disable() const { LPC_GPDMA->CH[number].CONFIG &= ~(1U << 0); } void clear_interrupts() const { LPC_GPDMA->INTTCCLR = (1U << number); LPC_GPDMA->INTERRCLR = (1U << number); } void set_handlers(const TCHandler tc_handler, const ErrHandler err_handler) const; void configure(const LLI& first_lli, const uint32_t config) const; const LLI* next_lli() const { return reinterpret_cast(LPC_GPDMA->CH[number].LLI); } private: const size_t number; }; } /* namespace channel */ constexpr std::array channels{{ {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, }}; static const gpdma_resources_t gpdma_resources = { .base = {.clk = &LPC_CGU->BASE_M4_CLK, .stat = &LPC_CCU1->BASE_STAT, .stat_mask = (1 << 3)}, .branch = {.cfg = &LPC_CCU1->CLK_M4_DMA_CFG, .stat = &LPC_CCU1->CLK_M4_DMA_STAT}, .reset = {.output_index = 19}, }; class Controller { public: void enable() const { base_clock_enable(&gpdma_resources.base); branch_clock_enable(&gpdma_resources.branch); peripheral_reset(&gpdma_resources.reset); LPC_GPDMA->CONFIG |= (1U << 0); } void disable() const { for (const auto& channel : channels) { channel.disable(); } LPC_GPDMA->CONFIG &= ~(1U << 0); peripheral_reset(&gpdma_resources.reset); branch_clock_disable(&gpdma_resources.branch); base_clock_disable(&gpdma_resources.base); } }; constexpr Controller controller; } /* namespace gpdma */ } /* namespace lpc43xx */ #endif /*__GPDMA_H__*/