Initial Release
This commit is contained in:
2
src/rp2_common/hardware_dma/CMakeLists.txt
Normal file
2
src/rp2_common/hardware_dma/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
pico_simple_hardware_target(dma)
|
||||
target_link_libraries(hardware_dma INTERFACE hardware_claim)
|
68
src/rp2_common/hardware_dma/dma.c
Normal file
68
src/rp2_common/hardware_dma/dma.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/claim.h"
|
||||
|
||||
#define DMA_CHAN_STRIDE (DMA_CH1_CTRL_TRIG_OFFSET - DMA_CH0_CTRL_TRIG_OFFSET)
|
||||
check_hw_size(dma_channel_hw_t, DMA_CHAN_STRIDE);
|
||||
check_hw_layout(dma_hw_t, abort, DMA_CHAN_ABORT_OFFSET);
|
||||
|
||||
// sanity check
|
||||
static_assert(__builtin_offsetof(dma_hw_t, ch[0].ctrl_trig) == DMA_CH0_CTRL_TRIG_OFFSET, "hw mismatch");
|
||||
static_assert(__builtin_offsetof(dma_hw_t, ch[1].ctrl_trig) == DMA_CH1_CTRL_TRIG_OFFSET, "hw mismatch");
|
||||
|
||||
static_assert(NUM_DMA_CHANNELS <= 16, "");
|
||||
static uint16_t _claimed;
|
||||
|
||||
void dma_channel_claim(uint channel) {
|
||||
check_dma_channel_param(channel);
|
||||
hw_claim_or_assert((uint8_t *) &_claimed, channel, "DMA channel %d is already claimed");
|
||||
}
|
||||
|
||||
void dma_claim_mask(uint32_t mask) {
|
||||
for(uint i = 0; mask; i++, mask >>= 1u) {
|
||||
if (mask & 1u) dma_channel_claim(i);
|
||||
}
|
||||
}
|
||||
|
||||
void dma_channel_unclaim(uint channel) {
|
||||
check_dma_channel_param(channel);
|
||||
hw_claim_clear((uint8_t *) &_claimed, channel);
|
||||
}
|
||||
|
||||
int dma_claim_unused_channel(bool required) {
|
||||
return hw_claim_unused_from_range((uint8_t*)&_claimed, required, 0, NUM_DMA_CHANNELS-1, "No DMA channels are available");
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
void print_dma_ctrl(dma_channel_hw_t *channel) {
|
||||
uint32_t ctrl = channel->ctrl_trig;
|
||||
int rgsz = (ctrl & DMA_CH0_CTRL_TRIG_RING_SIZE_BITS) >> DMA_CH0_CTRL_TRIG_RING_SIZE_LSB;
|
||||
printf("(%08x) ber %d rer %d wer %d busy %d trq %d cto %d rgsl %d rgsz %d inw %d inr %d sz %d hip %d en %d",
|
||||
(uint) ctrl,
|
||||
ctrl & DMA_CH0_CTRL_TRIG_AHB_ERROR_BITS ? 1 : 0,
|
||||
ctrl & DMA_CH0_CTRL_TRIG_READ_ERROR_BITS ? 1 : 0,
|
||||
ctrl & DMA_CH0_CTRL_TRIG_WRITE_ERROR_BITS ? 1 : 0,
|
||||
ctrl & DMA_CH0_CTRL_TRIG_BUSY_BITS ? 1 : 0,
|
||||
(int) ((ctrl & DMA_CH0_CTRL_TRIG_TREQ_SEL_BITS) >> DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB),
|
||||
(int) ((ctrl & DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) >> DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB),
|
||||
ctrl & DMA_CH0_CTRL_TRIG_RING_SEL_BITS ? 1 : 0,
|
||||
rgsz ? (1 << rgsz) : 0,
|
||||
ctrl & DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS ? 1 : 0,
|
||||
ctrl & DMA_CH0_CTRL_TRIG_INCR_READ_BITS ? 1 : 0,
|
||||
1 << ((ctrl & DMA_CH0_CTRL_TRIG_DATA_SIZE_BITS) >> DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB),
|
||||
ctrl & DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_BITS ? 1 : 0,
|
||||
ctrl & DMA_CH0_CTRL_TRIG_EN_BITS ? 1 : 0);
|
||||
}
|
||||
|
||||
void check_dma_channel_param_impl(uint channel) {
|
||||
valid_params_if(DMA, channel < NUM_DMA_CHANNELS);
|
||||
}
|
||||
|
||||
#endif
|
591
src/rp2_common/hardware_dma/include/hardware/dma.h
Normal file
591
src/rp2_common/hardware_dma/include/hardware/dma.h
Normal file
@ -0,0 +1,591 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _HARDWARE_DMA_H_
|
||||
#define _HARDWARE_DMA_H_
|
||||
|
||||
#include "pico.h"
|
||||
#include "hardware/structs/dma.h"
|
||||
#include "hardware/regs/dreq.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \file hardware/dma.h
|
||||
* \defgroup hardware_dma hardware_dma
|
||||
*
|
||||
* DMA Controller API
|
||||
*
|
||||
* The RP2040 Direct Memory Access (DMA) master performs bulk data transfers on a processor’s
|
||||
* behalf. This leaves processors free to attend to other tasks, or enter low-power sleep states. The
|
||||
* data throughput of the DMA is also significantly higher than one of RP2040’s processors.
|
||||
*
|
||||
* The DMA can perform one read access and one write access, up to 32 bits in size, every clock cycle.
|
||||
* There are 12 independent channels, which each supervise a sequence of bus transfers, usually in
|
||||
* one of the following scenarios:
|
||||
*
|
||||
* * Memory to peripheral
|
||||
* * Peripheral to memory
|
||||
* * Memory to memory
|
||||
*/
|
||||
|
||||
// this is not defined in generated dreq.h
|
||||
#define DREQ_FORCE 63
|
||||
|
||||
/*! \brief Mark a dma channel as used
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* Method for cooperative claiming of hardware. Will cause a panic if the channel
|
||||
* is already claimed. Use of this method by libraries detects accidental
|
||||
* configurations that would fail in unpredictable ways.
|
||||
*
|
||||
* \param channel the dma channel
|
||||
*/
|
||||
void dma_channel_claim(uint channel);
|
||||
|
||||
/*! \brief Mark multiple dma channels as used
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* Method for cooperative claiming of hardware. Will cause a panic if any of the channels
|
||||
* are already claimed. Use of this method by libraries detects accidental
|
||||
* configurations that would fail in unpredictable ways.
|
||||
*
|
||||
* \param channel_mask Bitfield of all required channels to claim (bit 0 == channel 0, bit 1 == channel 1 etc)
|
||||
*/
|
||||
void dma_claim_mask(uint32_t channel_mask);
|
||||
|
||||
/*! \brief Mark a dma channel as no longer used
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* Method for cooperative claiming of hardware.
|
||||
*
|
||||
* \param channel the dma channel to release
|
||||
*/
|
||||
void dma_channel_unclaim(uint channel);
|
||||
|
||||
/*! \brief Claim a free dma channel
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param required if true the function will panic if none are available
|
||||
* \return the dma channel number or -1 if required was false, and none were free
|
||||
*/
|
||||
int dma_claim_unused_channel(bool required);
|
||||
|
||||
/** \brief DMA channel configuration
|
||||
* \defgroup channel_config channel_config
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* A DMA channel needs to be configured, these functions provide handy helpers to set up configuration
|
||||
* structures. See \ref dma_channel_config
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \brief Enumeration of available DMA channel transfer sizes.
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* Names indicate the number of bits.
|
||||
*/
|
||||
enum dma_channel_transfer_size {
|
||||
DMA_SIZE_8 = 0, ///< Byte transfer (8 bits)
|
||||
DMA_SIZE_16 = 1, ///< Half word transfer (16 bits)
|
||||
DMA_SIZE_32 = 2 ///< Word transfer (32 bits)
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t ctrl;
|
||||
} dma_channel_config;
|
||||
|
||||
/*! \brief Set DMA channel read increment
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param incr True to enable read address increments, if false, each read will be from the same address
|
||||
* Usually disabled for peripheral to memory transfers
|
||||
*/
|
||||
static inline void channel_config_set_read_increment(dma_channel_config *c, bool incr) {
|
||||
c->ctrl = incr ? (c->ctrl | DMA_CH0_CTRL_TRIG_INCR_READ_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_INCR_READ_BITS);
|
||||
}
|
||||
|
||||
/*! \brief Set DMA channel write increment
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param incr True to enable write address increments, if false, each write will be to the same address
|
||||
* Usually disabled for memory to peripheral transfers
|
||||
* Usually disabled for memory to peripheral transfers
|
||||
*/
|
||||
static inline void channel_config_set_write_increment(dma_channel_config *c, bool incr) {
|
||||
c->ctrl = incr ? (c->ctrl | DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS);
|
||||
}
|
||||
|
||||
/*! \brief Select a transfer request signal
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* The channel uses the transfer request signal to pace its data transfer rate.
|
||||
* Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system).
|
||||
* 0x0 to 0x3a -> select DREQ n as TREQ
|
||||
* 0x3b -> Select Timer 0 as TREQ
|
||||
* 0x3c -> Select Timer 1 as TREQ
|
||||
* 0x3d -> Select Timer 2 as TREQ (Optional)
|
||||
* 0x3e -> Select Timer 3 as TREQ (Optional)
|
||||
* 0x3f -> Permanent request, for unpaced transfers.
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param dreq Source (see description)
|
||||
*/
|
||||
static inline void channel_config_set_dreq(dma_channel_config *c, uint dreq) {
|
||||
assert(dreq <= DREQ_FORCE);
|
||||
c->ctrl = (c->ctrl & ~DMA_CH0_CTRL_TRIG_TREQ_SEL_BITS) | (dreq << DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB);
|
||||
}
|
||||
|
||||
/*! \brief Set DMA channel completion channel
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* When this channel completes, it will trigger the channel indicated by chain_to. Disable by
|
||||
* setting chain_to to itself (the same channel)
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param chain_to Channel to trigger when this channel completes.
|
||||
*/
|
||||
static inline void channel_config_set_chain_to(dma_channel_config *c, uint chain_to) {
|
||||
assert(chain_to <= NUM_DMA_CHANNELS);
|
||||
c->ctrl = (c->ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (chain_to << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
|
||||
}
|
||||
|
||||
/*! \brief Set the size of each DMA bus transfer
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* Set the size of each bus transfer (byte/halfword/word). The read and write addresses
|
||||
* advance by the specific amount (1/2/4 bytes) with each transfer.
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param size See enum for possible values.
|
||||
*/
|
||||
static inline void channel_config_set_transfer_data_size(dma_channel_config *c, enum dma_channel_transfer_size size) {
|
||||
assert(size == DMA_SIZE_8 || size == DMA_SIZE_16 || size == DMA_SIZE_32);
|
||||
c->ctrl = (c->ctrl & ~DMA_CH0_CTRL_TRIG_DATA_SIZE_BITS) | (size << DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB);
|
||||
}
|
||||
|
||||
/*! \brief Set address wrapping parameters
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* Size of address wrap region. If 0, don’t wrap. For values n > 0, only the lower n bits of the address
|
||||
* will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned
|
||||
* ring buffers.
|
||||
* Ring sizes between 2 and 32768 bytes are possible (size_bits from 1 - 15)
|
||||
*
|
||||
* 0x0 -> No wrapping.
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param write True to apply to write addresses, false to apply to read addresses
|
||||
* \param size_bits 0 to disable wrapping. Otherwise the size in bits of the changing part of the address.
|
||||
* Effectively wraps the address on a (1 << size_bits) byte boundary.
|
||||
*/
|
||||
static inline void channel_config_set_ring(dma_channel_config *c, bool write, uint size_bits) {
|
||||
assert(size_bits < 32);
|
||||
c->ctrl = (c->ctrl & ~(DMA_CH0_CTRL_TRIG_RING_SIZE_BITS | DMA_CH0_CTRL_TRIG_RING_SEL_BITS)) |
|
||||
(size_bits << DMA_CH0_CTRL_TRIG_RING_SIZE_LSB) |
|
||||
(write ? DMA_CH0_CTRL_TRIG_RING_SEL_BITS : 0);
|
||||
}
|
||||
|
||||
/*! \brief Set DMA byte swapping
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* No effect for byte data, for halfword data, the two bytes of each halfword are
|
||||
* swapped. For word data, the four bytes of each word are swapped to reverse their order.
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param bswap True to enable byte swapping
|
||||
*/
|
||||
static inline void channel_config_set_bswap(dma_channel_config *c, bool bswap) {
|
||||
c->ctrl = bswap ? (c->ctrl | DMA_CH0_CTRL_TRIG_BSWAP_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_BSWAP_BITS);
|
||||
}
|
||||
|
||||
/*! \brief Set IRQ quiet mode
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead,
|
||||
* an IRQ is raised when NULL is written to a trigger register, indicating the end of a control
|
||||
* block chain.
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param irq_quiet True to enable quiet mode, false to disable.
|
||||
*/
|
||||
static inline void channel_config_set_irq_quiet(dma_channel_config *c, bool irq_quiet) {
|
||||
c->ctrl = irq_quiet ? (c->ctrl | DMA_CH0_CTRL_TRIG_IRQ_QUIET_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_IRQ_QUIET_BITS);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Enable/Disable the DMA channel
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* When false, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will
|
||||
* remain high if already high)
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param enable True to enable the DMA channel. When enabled, the channel will respond to triggering events, and start transferring data.
|
||||
*
|
||||
*/
|
||||
static inline void channel_config_set_enable(dma_channel_config *c, bool enable) {
|
||||
c->ctrl = enable ? (c->ctrl | DMA_CH0_CTRL_TRIG_EN_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_EN_BITS);
|
||||
}
|
||||
|
||||
/*! \brief Enable access to channel by sniff hardware.
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* Sniff HW must be enabled and have this channel selected.
|
||||
*
|
||||
* \param c Pointer to channel configuration data
|
||||
* \param sniff_enable True to enable the Sniff HW access to this DMA channel.
|
||||
*/
|
||||
static inline void channel_config_set_sniff_enable(dma_channel_config *c, bool sniff_enable) {
|
||||
c->ctrl = sniff_enable ? (c->ctrl | DMA_CH0_CTRL_TRIG_SNIFF_EN_BITS) : (c->ctrl &
|
||||
~DMA_CH0_CTRL_TRIG_SNIFF_EN_BITS);
|
||||
}
|
||||
|
||||
/*! \brief Get the default channel configuration for a given channel
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* Setting | Default
|
||||
* --------|--------
|
||||
* Read Increment | true
|
||||
* Write Increment | false
|
||||
* DReq | DREQ_FORCE
|
||||
* Chain to | self
|
||||
* Data size | DMA_SIZE_32
|
||||
* Ring | write=false, size=0 (i.e. off)
|
||||
* Byte Swap | false
|
||||
* Quiet IRQs | false
|
||||
* Channel Enable | true
|
||||
* Sniff Enable | false
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \return the default configuration which can then be modified.
|
||||
*/
|
||||
static inline dma_channel_config dma_channel_get_default_config(uint channel) {
|
||||
dma_channel_config c = {0};
|
||||
channel_config_set_read_increment(&c, true);
|
||||
channel_config_set_write_increment(&c, false);
|
||||
channel_config_set_dreq(&c, DREQ_FORCE);
|
||||
channel_config_set_chain_to(&c, channel);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
|
||||
channel_config_set_ring(&c, false, 0);
|
||||
channel_config_set_bswap(&c, false);
|
||||
channel_config_set_irq_quiet(&c, false);
|
||||
channel_config_set_enable(&c, true);
|
||||
channel_config_set_sniff_enable(&c, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*! \brief Get the current configuration for the specified channel.
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \return The current configuration as read from the HW register (not cached)
|
||||
*/
|
||||
static inline dma_channel_config dma_get_channel_config(uint channel) {
|
||||
dma_channel_config c;
|
||||
c.ctrl = dma_channel_hw_addr(channel)->ctrl_trig;
|
||||
return c;
|
||||
}
|
||||
|
||||
/*! \brief Get the raw configuration register from a channel configuration
|
||||
* \ingroup channel_config
|
||||
*
|
||||
* \param config Pointer to a config structure.
|
||||
* \return Register content
|
||||
*/
|
||||
static inline uint32_t channel_config_get_ctrl_value(const dma_channel_config *config) {
|
||||
return config->ctrl;
|
||||
}
|
||||
|
||||
/*! \brief Set a channel configuration
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param config Pointer to a config structure with required configuration
|
||||
* \param trigger True to trigger the transfer immediately
|
||||
*/
|
||||
static inline void dma_channel_set_config(uint channel, const dma_channel_config *config, bool trigger) {
|
||||
// Don't use CTRL_TRIG since we don't want to start a transfer
|
||||
if (!trigger) {
|
||||
dma_channel_hw_addr(channel)->al1_ctrl = channel_config_get_ctrl_value(config);
|
||||
} else {
|
||||
dma_channel_hw_addr(channel)->ctrl_trig = channel_config_get_ctrl_value(config);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Set the DMA initial read address.
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param read_addr Initial read address of transfer.
|
||||
* \param trigger True to start the transfer immediately
|
||||
*/
|
||||
static inline void dma_channel_set_read_addr(uint channel, const volatile void *read_addr, bool trigger) {
|
||||
if (!trigger) {
|
||||
dma_channel_hw_addr(channel)->read_addr = (uintptr_t) read_addr;
|
||||
} else {
|
||||
dma_channel_hw_addr(channel)->al3_read_addr_trig = (uintptr_t) read_addr;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Set the DMA initial read address
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param write_addr Initial write address of transfer.
|
||||
* \param trigger True to start the transfer immediately
|
||||
*/
|
||||
static inline void dma_channel_set_write_addr(uint channel, volatile void *write_addr, bool trigger) {
|
||||
if (!trigger) {
|
||||
dma_channel_hw_addr(channel)->write_addr = (uintptr_t) write_addr;
|
||||
} else {
|
||||
dma_channel_hw_addr(channel)->al2_write_addr_trig = (uintptr_t) write_addr;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Set the number of bus transfers the channel will do
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param trans_count The number of transfers (not NOT bytes, see channel_config_set_transfer_data_size)
|
||||
* \param trigger True to start the transfer immediately
|
||||
*/
|
||||
static inline void dma_channel_set_trans_count(uint channel, uint32_t trans_count, bool trigger) {
|
||||
if (!trigger) {
|
||||
dma_channel_hw_addr(channel)->transfer_count = trans_count;
|
||||
} else {
|
||||
dma_channel_hw_addr(channel)->al1_transfer_count_trig = trans_count;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Configure all DMA parameters and optionally start transfer
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param config Pointer to DMA config structure
|
||||
* \param write_addr Initial write address
|
||||
* \param read_addr Initial read address
|
||||
* \param transfer_count Number of transfers to perform
|
||||
* \param trigger True to start the transfer immediately
|
||||
*/
|
||||
static inline void dma_channel_configure(uint channel, const dma_channel_config *config, volatile void *write_addr,
|
||||
const volatile void *read_addr,
|
||||
uint transfer_count, bool trigger) {
|
||||
dma_channel_set_read_addr(channel, read_addr, false);
|
||||
dma_channel_set_write_addr(channel, write_addr, false);
|
||||
dma_channel_set_trans_count(channel, transfer_count, false);
|
||||
dma_channel_set_config(channel, config, trigger);
|
||||
}
|
||||
|
||||
/*! \brief Start a DMA transfer from a buffer immediately
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param read_addr Sets the initial read address
|
||||
* \param transfer_count Number of transfers to make. Not bytes, but the number of transfers of channel_config_set_transfer_data_size() to be sent.
|
||||
*/
|
||||
inline static void __attribute__((always_inline)) dma_channel_transfer_from_buffer_now(uint channel, void *read_addr,
|
||||
uint32_t transfer_count) {
|
||||
// check_dma_channel_param(channel);
|
||||
dma_channel_hw_t *hw = dma_channel_hw_addr(channel);
|
||||
hw->read_addr = (uintptr_t) read_addr;
|
||||
hw->al1_transfer_count_trig = transfer_count;
|
||||
}
|
||||
|
||||
/*! \brief Start a DMA transfer to a buffer immediately
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param write_addr Sets the initial write address
|
||||
* \param transfer_count Number of transfers to make. Not bytes, but the number of transfers of channel_config_set_transfer_data_size() to be sent.
|
||||
*/
|
||||
inline static void dma_channel_transfer_to_buffer_now(uint channel, void *write_addr, uint32_t transfer_count) {
|
||||
dma_channel_hw_t *hw = dma_channel_hw_addr(channel);
|
||||
hw->write_addr = (uintptr_t) write_addr;
|
||||
hw->al1_transfer_count_trig = transfer_count;
|
||||
}
|
||||
|
||||
/*! \brief Start one or more channels simultaneously
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param chan_mask Bitmask of all the channels requiring starting. Channel 0 = bit 0, channel 1 = bit 1 etc.
|
||||
*/
|
||||
static inline void dma_start_channel_mask(uint32_t chan_mask) {
|
||||
valid_params_if(DMA, chan_mask && chan_mask < (1u << NUM_DMA_CHANNELS));
|
||||
dma_hw->multi_channel_trigger = chan_mask;
|
||||
}
|
||||
|
||||
/*! \brief Start a single DMA channel
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
*/
|
||||
static inline void dma_channel_start(uint channel) {
|
||||
dma_start_channel_mask(1u << channel);
|
||||
}
|
||||
|
||||
/*! \brief Stop a DMA transfer
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* Function will only return once the DMA has stopped.
|
||||
*
|
||||
* \param channel DMA channel
|
||||
*/
|
||||
static inline void dma_channel_abort(uint channel) {
|
||||
check_dma_channel_param(channel);
|
||||
dma_hw->abort = 1u << channel;
|
||||
// Bit will go 0 once channel has reached safe state
|
||||
// (i.e. any in-flight transfers have retired)
|
||||
while (dma_hw->abort & (1ul << channel)) tight_loop_contents();
|
||||
}
|
||||
|
||||
/*! \brief Enable single DMA channel interrupt 0
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param enabled true to enable interrupt 0 on specified channel, false to disable.
|
||||
*/
|
||||
static inline void dma_channel_set_irq0_enabled(uint channel, bool enabled) {
|
||||
check_dma_channel_param(channel);
|
||||
check_hw_layout(dma_hw_t, inte0, DMA_INTE0_OFFSET);
|
||||
if (enabled)
|
||||
hw_set_bits(&dma_hw->inte0, 1u << channel);
|
||||
else
|
||||
hw_clear_bits(&dma_hw->inte0, 1u << channel);
|
||||
}
|
||||
|
||||
/*! \brief Enable multiple DMA channels interrupt 0
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel_mask Bitmask of all the channels to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
|
||||
* \param enabled true to enable all the interrupts specified in the mask, false to disable all the interrupts specified in the mask.
|
||||
*/
|
||||
static inline void dma_set_irq0_channel_mask_enabled(uint32_t channel_mask, bool enabled) {
|
||||
if (enabled) {
|
||||
hw_set_bits(&dma_hw->inte0, channel_mask);
|
||||
} else {
|
||||
hw_clear_bits(&dma_hw->inte0, channel_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Enable single DMA channel interrupt 1
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param enabled true to enable interrupt 1 on specified channel, false to disable.
|
||||
*/
|
||||
static inline void dma_channel_set_irq1_enabled(uint channel, bool enabled) {
|
||||
check_dma_channel_param(channel);
|
||||
check_hw_layout(dma_hw_t, inte1, DMA_INTE1_OFFSET);
|
||||
if (enabled)
|
||||
hw_set_bits(&dma_hw->inte1, 1u << channel);
|
||||
else
|
||||
hw_clear_bits(&dma_hw->inte1, 1u << channel);
|
||||
}
|
||||
|
||||
/*! \brief Enable multiple DMA channels interrupt 0
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel_mask Bitmask of all the channels to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
|
||||
* \param enabled true to enable all the interrupts specified in the mask, false to disable all the interrupts specified in the mask.
|
||||
*/
|
||||
static inline void dma_set_irq1_channel_mask_enabled(uint32_t channel_mask, bool enabled) {
|
||||
if (enabled) {
|
||||
hw_set_bits(&dma_hw->inte1, channel_mask);
|
||||
} else {
|
||||
hw_clear_bits(&dma_hw->inte1, channel_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Check if DMA channel is busy
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \return true if the channel is currently busy
|
||||
*/
|
||||
inline static bool dma_channel_is_busy(uint channel) {
|
||||
check_dma_channel_param(channel);
|
||||
return !!(dma_hw->ch[channel].al1_ctrl & DMA_CH0_CTRL_TRIG_BUSY_BITS);
|
||||
}
|
||||
|
||||
/*! \brief Wait for a DMA channel transfer to complete
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* \param channel DMA channel
|
||||
*/
|
||||
inline static void dma_channel_wait_for_finish_blocking(uint channel) {
|
||||
while (dma_channel_is_busy(channel)) tight_loop_contents();
|
||||
}
|
||||
|
||||
/*! \brief Enable the DMA sniffing targeting the specified channel
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* The mode can be one of the following:
|
||||
*
|
||||
* Mode | Function
|
||||
* -----|---------
|
||||
* 0x0 | Calculate a CRC-32 (IEEE802.3 polynomial)
|
||||
* 0x1 | Calculate a CRC-32 (IEEE802.3 polynomial) with bit reversed data
|
||||
* 0x2 | Calculate a CRC-16-CCITT
|
||||
* 0x3 | Calculate a CRC-16-CCITT with bit reversed data
|
||||
* 0xe | XOR reduction over all data. == 1 if the total 1 population count is odd.
|
||||
* 0xf | Calculate a simple 32-bit checksum (addition with a 32 bit accumulator)
|
||||
*
|
||||
* \param channel DMA channel
|
||||
* \param mode See description
|
||||
* \param force_channel_enable Set true to also turn on sniffing in the channel configuration (this
|
||||
* is usually what you want, but sometimes you might have a chain DMA with only certain segments
|
||||
* of the chain sniffed, in which case you might pass false).
|
||||
*/
|
||||
inline static void dma_sniffer_enable(uint channel, uint mode, bool force_channel_enable) {
|
||||
check_dma_channel_param(channel);
|
||||
check_hw_layout(dma_hw_t, sniff_ctrl, DMA_SNIFF_CTRL_OFFSET);
|
||||
if (force_channel_enable) {
|
||||
hw_set_bits(&dma_hw->ch[channel].al1_ctrl, DMA_CH0_CTRL_TRIG_SNIFF_EN_BITS);
|
||||
}
|
||||
dma_hw->sniff_ctrl = ((channel << DMA_SNIFF_CTRL_DMACH_LSB) & DMA_SNIFF_CTRL_DMACH_BITS) |
|
||||
((mode << DMA_SNIFF_CTRL_CALC_LSB) & DMA_SNIFF_CTRL_CALC_BITS) |
|
||||
DMA_SNIFF_CTRL_EN_BITS;
|
||||
}
|
||||
|
||||
/*! \brief Enable the Sniffer byte swap function
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
* Locally perform a byte reverse on the sniffed data, before feeding into checksum.
|
||||
*
|
||||
* Note that the sniff hardware is downstream of the DMA channel byteswap performed in the
|
||||
* read master: if channel_config_set_bswap() and dma_sniffer_set_byte_swap_enabled() are both enabled,
|
||||
* their effects cancel from the sniffer’s point of view.
|
||||
*
|
||||
* \param swap Set true to enable byte swapping
|
||||
*/
|
||||
inline static void dma_sniffer_set_byte_swap_enabled(bool swap) {
|
||||
if (swap)
|
||||
hw_set_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_BSWAP_BITS);
|
||||
else
|
||||
hw_clear_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_BSWAP_BITS);
|
||||
}
|
||||
|
||||
/*! \brief Disable the DMA sniffer
|
||||
* \ingroup hardware_dma
|
||||
*
|
||||
*/
|
||||
inline static void dma_sniffer_disable() {
|
||||
dma_hw->sniff_ctrl = 0;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void print_dma_ctrl(dma_channel_hw_t *channel);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user