cpu/stm32/periph_eth: Cleanup & fix DMA descriptor
- Add missing `volatile` to DMA descriptor, as memory is also accessed by the
DMA without knowledge of the compiler
- Dropped `__attribute__((packed))` from DMA descriptor
- The DMA descriptor fields need to be aligned on word boundries to
properly function
- The compiler can now more efficiently access the fields (safes ~300 B ROM)
- Moved the DMA descriptor struct and the flags to `periph_cpu.h`
- This allows Doxygen documentation being build for it
- Those types and fields are needed for a future PTP implementation
- Renamed DMA descriptor flags
- They now reflect to which field in the DMA descriptor they refer to, so
that confusion is avoided
- Added documentation to the DMA descriptor and the corresponding flags
This commit is contained in:
parent
a7cf50eb03
commit
a5dbec33d9
@ -1017,6 +1017,97 @@ typedef struct {
|
|||||||
supported by all modes. */
|
supported by all modes. */
|
||||||
} eth_conf_t;
|
} eth_conf_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Layout of enhanced RX/TX DMA descriptor
|
||||||
|
*
|
||||||
|
* @note Don't confuse this with the normal RX/TX descriptor format.
|
||||||
|
* @warning The content of the status and control bits is different for RX and
|
||||||
|
* TX DMA descriptors
|
||||||
|
*/
|
||||||
|
typedef struct eth_dma_desc {
|
||||||
|
volatile uint32_t status; /**< Mostly status bits, some control bits */
|
||||||
|
volatile uint32_t control; /**< Control bits */
|
||||||
|
char * volatile buffer_addr; /**< RX/TX buffer */
|
||||||
|
struct eth_dma_desc * volatile desc_next; /**< Address of next DMA descriptor */
|
||||||
|
volatile uint32_t reserved1_ext; /**< RX: Extended status, TX: reserved */
|
||||||
|
volatile uint32_t reserved2; /**< Reserved for future use */
|
||||||
|
/**
|
||||||
|
* @brief Sub-second part of PTP timestamp of transmitted / sent frame
|
||||||
|
*
|
||||||
|
* For TX: If PTP timestamping is enabled and the TTSE bit in the
|
||||||
|
* transmit descriptor word 0 (struct eth_dma_desc::status) is set, the
|
||||||
|
* MAC will store the PTP timestamp of when the Start of Frame Delimiter
|
||||||
|
* was sent. The TTSS bit is send by the hardware if the timestamp was
|
||||||
|
* correctly set.
|
||||||
|
*
|
||||||
|
* For RX: If PTP timestamping is enabled, the timestamp of all received
|
||||||
|
* frames is captured.
|
||||||
|
*/
|
||||||
|
volatile uint32_t ts_low;
|
||||||
|
volatile uint32_t ts_high; /**< Second part of PTP timestamp */
|
||||||
|
} edma_desc_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Flags in the status word of the Ethernet enhanced RX DMA descriptor
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define RX_DESC_STAT_LS (BIT8) /**< If set, descriptor is the last of a frame */
|
||||||
|
#define RX_DESC_STAT_FS (BIT9) /**< If set, descriptor is the first of a frame */
|
||||||
|
/**
|
||||||
|
* @brief Frame length
|
||||||
|
*
|
||||||
|
* The length of the frame in host memory order including CRC. Only valid if
|
||||||
|
* @ref RX_DESC_STAT_LS is set and @ref RX_DESC_STAT_DE is not set.
|
||||||
|
*/
|
||||||
|
#define RX_DESC_STAT_FL (0x3FFF0000) /* bits 16-29 */
|
||||||
|
#define RX_DESC_STAT_DE (BIT14) /**< If set, a frame too large to fit buffers given by descriptors was received */
|
||||||
|
#define RX_DESC_STAT_OWN (BIT31) /**< If set, descriptor is owned by DMA, otherwise by CPU */
|
||||||
|
/** @} */
|
||||||
|
/**
|
||||||
|
* @name Flags in the control word of the Ethernet enhanced RX DMA descriptor
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @brief Indicates if RDES3 points to the next DMA descriptor (1), or to a second buffer (0)
|
||||||
|
*
|
||||||
|
* If the bit is set, RDES3 (@ref edma_desc_t::desc_next) will point to the
|
||||||
|
* next DMA descriptor rather than to a second frame-segment buffer. This is
|
||||||
|
* always set by the driver
|
||||||
|
*/
|
||||||
|
#define RX_DESC_CTRL_RCH (BIT14)
|
||||||
|
/** @} */
|
||||||
|
/**
|
||||||
|
* @name Flags in the status word of the Ethernet enhanced TX DMA descriptor
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define TX_DESC_STAT_TTSS (BIT17) /**< If set, the descriptor contains a valid PTP timestamp */
|
||||||
|
/**
|
||||||
|
* @brief Indicates if TDES3 points to the next DMA descriptor (1), or to a second buffer (0)
|
||||||
|
*
|
||||||
|
* If the bit is set, TDES3 (@ref edma_desc_t::desc_next) will point to the
|
||||||
|
* next DMA descriptor rather than to a second frame-segment buffer. This is
|
||||||
|
* always set by the driver
|
||||||
|
*/
|
||||||
|
#define TX_DESC_STAT_TCH (BIT20)
|
||||||
|
/**
|
||||||
|
* @brief Checksum insertion control
|
||||||
|
*
|
||||||
|
* | Value | Meaning |
|
||||||
|
* |:------ |:----------------------------------------------------------------------------- |
|
||||||
|
* | `0b00` | Checksum insertion disabled |
|
||||||
|
* | `0b01` | Calculate and insert checksum in IPv4 header |
|
||||||
|
* | `0b10` | Calculate and insert IPv4 checksum, insert pre-calculated payload checksum |
|
||||||
|
* | `0b11 | Calculated and insert both IPv4 and payload checksum |
|
||||||
|
*/
|
||||||
|
#define TX_DESC_STAT_CIC (BIT22 | BIT23)
|
||||||
|
#define TX_DESC_STAT_TTSE (BIT25) /**< If set, an PTP timestamp is added to the descriptor after TX completed */
|
||||||
|
#define TX_DESC_STAT_FS (BIT28) /**< If set, buffer contains first segment of frame to transmit */
|
||||||
|
#define TX_DESC_STAT_LS (BIT29) /**< If set, buffer contains last segment of frame to transmit */
|
||||||
|
#define TX_DESC_STAT_IC (BIT30) /**< If set, trigger IRQ on completion */
|
||||||
|
#define TX_DESC_STAT_OWN (BIT31) /**< If set, descriptor is owned by DMA, otherwise by CPU */
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name Ethernet PHY Common Registers
|
* @name Ethernet PHY Common Registers
|
||||||
* @{
|
* @{
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bitarithm.h"
|
||||||
#include "mutex.h"
|
#include "mutex.h"
|
||||||
#include "luid.h"
|
#include "luid.h"
|
||||||
|
|
||||||
@ -30,7 +31,6 @@
|
|||||||
#define ENABLE_DEBUG (0)
|
#define ENABLE_DEBUG (0)
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
|
|
||||||
/* Set the value of the divider with the clock configured */
|
/* Set the value of the divider with the clock configured */
|
||||||
#if !defined(CLOCK_CORECLOCK) || CLOCK_CORECLOCK < (20000000U)
|
#if !defined(CLOCK_CORECLOCK) || CLOCK_CORECLOCK < (20000000U)
|
||||||
#error This peripheral requires a CORECLOCK of at least 20MHz
|
#error This peripheral requires a CORECLOCK of at least 20MHz
|
||||||
@ -46,31 +46,6 @@
|
|||||||
#define CLOCK_RANGE ETH_MACMIIAR_CR_Div102
|
#define CLOCK_RANGE ETH_MACMIIAR_CR_Div102
|
||||||
#endif /* CLOCK_CORECLOCK < (20000000U) */
|
#endif /* CLOCK_CORECLOCK < (20000000U) */
|
||||||
|
|
||||||
/* Internal flags for the DMA descriptors */
|
|
||||||
#define DESC_OWN (0x80000000)
|
|
||||||
#define RX_DESC_FL (0x3FFF0000)
|
|
||||||
#define RX_DESC_FS (0x00000200)
|
|
||||||
#define RX_DESC_LS (0x00000100)
|
|
||||||
#define RX_DESC_RCH (0x00004000)
|
|
||||||
#define TX_DESC_TCH (0x00100000)
|
|
||||||
#define TX_DESC_IC (0x40000000)
|
|
||||||
#define TX_DESC_CIC (0x00C00000)
|
|
||||||
#define TX_DESC_LS (0x20000000)
|
|
||||||
#define TX_DESC_FS (0x10000000)
|
|
||||||
|
|
||||||
struct eth_dma_desc {
|
|
||||||
uint32_t status;
|
|
||||||
uint32_t control;
|
|
||||||
char *buffer_addr;
|
|
||||||
struct eth_dma_desc *desc_next;
|
|
||||||
uint32_t reserved1_ext;
|
|
||||||
uint32_t reserved2;
|
|
||||||
uint32_t ts_low;
|
|
||||||
uint32_t ts_high;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
typedef struct eth_dma_desc edma_desc_t;
|
|
||||||
|
|
||||||
/* Descriptors */
|
/* Descriptors */
|
||||||
static edma_desc_t rx_desc[ETH_RX_BUFFER_COUNT];
|
static edma_desc_t rx_desc[ETH_RX_BUFFER_COUNT];
|
||||||
static edma_desc_t tx_desc[ETH_TX_BUFFER_COUNT];
|
static edma_desc_t tx_desc[ETH_TX_BUFFER_COUNT];
|
||||||
@ -142,8 +117,8 @@ static void _init_buffer(void)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < ETH_RX_BUFFER_COUNT; i++) {
|
for (i = 0; i < ETH_RX_BUFFER_COUNT; i++) {
|
||||||
rx_desc[i].status = DESC_OWN;
|
rx_desc[i].status = RX_DESC_STAT_OWN;
|
||||||
rx_desc[i].control = RX_DESC_RCH | (ETH_RX_BUFFER_SIZE & 0x0fff);
|
rx_desc[i].control = RX_DESC_CTRL_RCH | (ETH_RX_BUFFER_SIZE & 0x0fff);
|
||||||
rx_desc[i].buffer_addr = &rx_buffer[i][0];
|
rx_desc[i].buffer_addr = &rx_buffer[i][0];
|
||||||
if((i+1) < ETH_RX_BUFFER_COUNT) {
|
if((i+1) < ETH_RX_BUFFER_COUNT) {
|
||||||
rx_desc[i].desc_next = &rx_desc[i + 1];
|
rx_desc[i].desc_next = &rx_desc[i + 1];
|
||||||
@ -152,7 +127,7 @@ static void _init_buffer(void)
|
|||||||
rx_desc[i - 1].desc_next = &rx_desc[0];
|
rx_desc[i - 1].desc_next = &rx_desc[0];
|
||||||
|
|
||||||
for (i = 0; i < ETH_TX_BUFFER_COUNT; i++) {
|
for (i = 0; i < ETH_TX_BUFFER_COUNT; i++) {
|
||||||
tx_desc[i].status = TX_DESC_TCH | TX_DESC_CIC;
|
tx_desc[i].status = TX_DESC_STAT_TCH | TX_DESC_STAT_CIC;
|
||||||
tx_desc[i].buffer_addr = &tx_buffer[i][0];
|
tx_desc[i].buffer_addr = &tx_buffer[i][0];
|
||||||
if ((i + 1) < ETH_RX_BUFFER_COUNT) {
|
if ((i + 1) < ETH_RX_BUFFER_COUNT) {
|
||||||
tx_desc[i].desc_next = &tx_desc[i + 1];
|
tx_desc[i].desc_next = &tx_desc[i + 1];
|
||||||
@ -164,8 +139,8 @@ static void _init_buffer(void)
|
|||||||
rx_curr = &rx_desc[0];
|
rx_curr = &rx_desc[0];
|
||||||
tx_curr = &tx_desc[0];
|
tx_curr = &tx_desc[0];
|
||||||
|
|
||||||
ETH->DMARDLAR = (uint32_t)rx_curr;
|
ETH->DMARDLAR = (uintptr_t)rx_curr;
|
||||||
ETH->DMATDLAR = (uint32_t)tx_curr;
|
ETH->DMATDLAR = (uintptr_t)tx_curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stm32_eth_init(void)
|
int stm32_eth_init(void)
|
||||||
@ -262,7 +237,7 @@ int stm32_eth_send(const struct iolist *iolist)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* block until there's an available descriptor */
|
/* block until there's an available descriptor */
|
||||||
while (tx_curr->status & DESC_OWN) {
|
while (tx_curr->status & TX_DESC_STAT_OWN) {
|
||||||
DEBUG("stm32_eth: not avail\n");
|
DEBUG("stm32_eth: not avail\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,8 +246,10 @@ int stm32_eth_send(const struct iolist *iolist)
|
|||||||
|
|
||||||
dma_acquire(eth_config.dma);
|
dma_acquire(eth_config.dma);
|
||||||
for (; iolist; iolist = iolist->iol_next) {
|
for (; iolist; iolist = iolist->iol_next) {
|
||||||
ret += dma_transfer(eth_config.dma, eth_config.dma_chan, iolist->iol_base,
|
ret += dma_transfer(
|
||||||
tx_curr->buffer_addr+ret, iolist->iol_len, DMA_MEM_TO_MEM, DMA_INC_BOTH_ADDR);
|
eth_config.dma, eth_config.dma_chan,
|
||||||
|
iolist->iol_base, tx_curr->buffer_addr + ret, iolist->iol_len,
|
||||||
|
DMA_MEM_TO_MEM, DMA_INC_BOTH_ADDR);
|
||||||
}
|
}
|
||||||
|
|
||||||
dma_release(eth_config.dma);
|
dma_release(eth_config.dma);
|
||||||
@ -283,11 +260,11 @@ int stm32_eth_send(const struct iolist *iolist)
|
|||||||
tx_curr->control = (len & 0x1fff);
|
tx_curr->control = (len & 0x1fff);
|
||||||
|
|
||||||
/* set flags for first and last frames */
|
/* set flags for first and last frames */
|
||||||
tx_curr->status |= TX_DESC_FS;
|
tx_curr->status |= TX_DESC_STAT_FS;
|
||||||
tx_curr->status |= TX_DESC_LS | TX_DESC_IC;
|
tx_curr->status |= TX_DESC_STAT_LS | TX_DESC_STAT_IC;
|
||||||
|
|
||||||
/* give the descriptors to the DMA */
|
/* give the descriptors to the DMA */
|
||||||
tx_curr->status |= DESC_OWN;
|
tx_curr->status |= TX_DESC_STAT_OWN;
|
||||||
tx_curr = tx_curr->desc_next;
|
tx_curr = tx_curr->desc_next;
|
||||||
|
|
||||||
/* start tx */
|
/* start tx */
|
||||||
@ -305,7 +282,7 @@ static int _try_receive(char *data, int max_len, int block)
|
|||||||
for (int i = 0; i < ETH_RX_BUFFER_COUNT && len == 0; i++) {
|
for (int i = 0; i < ETH_RX_BUFFER_COUNT && len == 0; i++) {
|
||||||
/* try receiving, if the block is set, simply wait for the rest of
|
/* try receiving, if the block is set, simply wait for the rest of
|
||||||
* the packet to complete, otherwise just break */
|
* the packet to complete, otherwise just break */
|
||||||
while (p->status & DESC_OWN) {
|
while (p->status & RX_DESC_STAT_OWN) {
|
||||||
if (!block) {
|
if (!block) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -313,7 +290,7 @@ static int _try_receive(char *data, int max_len, int block)
|
|||||||
|
|
||||||
/* amount of data to copy */
|
/* amount of data to copy */
|
||||||
copy = ETH_RX_BUFFER_SIZE;
|
copy = ETH_RX_BUFFER_SIZE;
|
||||||
if (p->status & (RX_DESC_LS | RX_DESC_FL)) {
|
if (p->status & (RX_DESC_STAT_LS | RX_DESC_STAT_FL)) {
|
||||||
len = ((p->status >> 16) & 0x3FFF) - 4;
|
len = ((p->status >> 16) & 0x3FFF) - 4;
|
||||||
copy = len - copied;
|
copy = len - copied;
|
||||||
}
|
}
|
||||||
@ -327,7 +304,7 @@ static int _try_receive(char *data, int max_len, int block)
|
|||||||
else if (max_len < copy) {
|
else if (max_len < copy) {
|
||||||
len = -1;
|
len = -1;
|
||||||
}
|
}
|
||||||
p->status = DESC_OWN;
|
p->status = RX_DESC_STAT_OWN;
|
||||||
}
|
}
|
||||||
p = p->desc_next;
|
p = p->desc_next;
|
||||||
}
|
}
|
||||||
@ -351,7 +328,7 @@ int stm32_eth_receive_blocking(char *data, unsigned max_len)
|
|||||||
|
|
||||||
int stm32_eth_get_rx_status_owned(void)
|
int stm32_eth_get_rx_status_owned(void)
|
||||||
{
|
{
|
||||||
return (!(rx_curr->status & DESC_OWN));
|
return (!(rx_curr->status & RX_DESC_STAT_OWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
void stm32_eth_isr_eth_wkup(void)
|
void stm32_eth_isr_eth_wkup(void)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user