diff options
Diffstat (limited to 'c/src/libchip/network/dwmac-core.c')
-rw-r--r-- | c/src/libchip/network/dwmac-core.c | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/c/src/libchip/network/dwmac-core.c b/c/src/libchip/network/dwmac-core.c new file mode 100644 index 0000000000..1050751e57 --- /dev/null +++ b/c/src/libchip/network/dwmac-core.c @@ -0,0 +1,406 @@ +/** + * @file + * + * @brief DWMAC 10/100/1000 Network Interface Controllers Core Handling + * + * DWMAC 10/100/1000 on-chip Synopsys IP Ethernet controllers. + * Driver core handling. + */ + +/* + * Copyright (c) 2013 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include "dwmac-core.h" +#include "dwmac-common.h" +#include "dwmac-regs.h" + +#undef DWMAC_CORE_DEBUG +#ifdef DWMAC_CORE_DEBUG +#define DWMAC_CORE_PRINT_DBG( fmt, args ... ) printk( fmt, ## args ) +#else +#define DWMAC_CORE_PRINT_DBG( fmt, args ... ) do { } while ( 0 ) +#endif + +/* DMA default interrupt masks */ +#define DWMAC_CORE_INTR_ENABLE_DEFAULT_MASK_RX \ + ( \ + DMAGRP_INTERRUPT_ENABLE_NIE \ + | DMAGRP_INTERRUPT_ENABLE_RIE \ + ) +#define DWMAC_CORE_INTR_ENABLE_DEFAULT_MASK_TX \ + ( \ + DMAGRP_INTERRUPT_ENABLE_NIE \ + | DMAGRP_INTERRUPT_ENABLE_TIE \ + | DMAGRP_INTERRUPT_ENABLE_FBE \ + | DMAGRP_INTERRUPT_ENABLE_UNE \ + | DMAGRP_INTERRUPT_ENABLE_AIE \ + ) + +#define DWMAC_CORE_INTR_STATUS_DEFAULT_MASK_RX \ + ( \ + DMAGRP_STATUS_NIS \ + | DMAGRP_STATUS_RI \ + ) +#define DWMAC_CORE_INTR_STATUS_DEFAULT_MASK_TX \ + ( \ + DMAGRP_STATUS_NIS \ + | DMAGRP_STATUS_TI \ + | DMAGRP_STATUS_FBI \ + | DMAGRP_STATUS_UNF \ + | DMAGRP_STATUS_AIS \ + ) + +/* CSR1 enables the transmit DMA to check for new descriptor */ +void dwmac_core_dma_restart_tx( dwmac_common_context *self ) +{ + self->dmagrp->transmit_poll_demand = 1; +} + +void dwmac_core_enable_dma_irq_tx( dwmac_common_context *self ) +{ + self->dmagrp->interrupt_enable |= DWMAC_CORE_INTR_ENABLE_DEFAULT_MASK_TX; +} + +void dwmac_core_enable_dma_irq_rx( dwmac_common_context *self ) +{ + self->dmagrp->interrupt_enable |= DWMAC_CORE_INTR_ENABLE_DEFAULT_MASK_RX; +} + +void dwmac_core_disable_dma_irq_tx( dwmac_common_context *self ) +{ + self->dmagrp->interrupt_enable &= ~DWMAC_CORE_INTR_ENABLE_DEFAULT_MASK_TX; +} + +void dwmac_core_reset_dma_irq_status_tx( dwmac_common_context *self ) +{ + self->dmagrp->status = DWMAC_CORE_INTR_STATUS_DEFAULT_MASK_TX; +} + +void dwmac_core_reset_dma_irq_status_rx( dwmac_common_context *self ) +{ + self->dmagrp->status = DWMAC_CORE_INTR_STATUS_DEFAULT_MASK_RX; +} + +void dwmac_core_disable_dma_irq_rx( dwmac_common_context *self ) +{ + self->dmagrp->interrupt_enable &= ~DWMAC_CORE_INTR_ENABLE_DEFAULT_MASK_RX; +} + +void dwmac_core_dma_start_tx( dwmac_common_context *self ) +{ + self->dmagrp->operation_mode |= DMAGRP_OPERATION_MODE_ST; +} + +void dwmac_core_dma_stop_tx( dwmac_common_context *self ) +{ + self->dmagrp->operation_mode &= ~DMAGRP_OPERATION_MODE_ST; +} + +void dwmac_core_dma_start_rx( dwmac_common_context *self ) +{ + self->dmagrp->operation_mode |= DMAGRP_OPERATION_MODE_SR; +} + +void dwmac_core_dma_stop_rx( dwmac_common_context *self ) +{ + self->dmagrp->operation_mode &= ~DMAGRP_OPERATION_MODE_SR; +} + +void dwmac_core_dma_restart_rx( dwmac_common_context *self ) +{ + self->dmagrp->receive_poll_demand = 1; +} + +#ifdef DWMAC_CORE_DEBUG +static void show_tx_process_state( const uint32_t status ) +{ + const uint32_t STATE = DMAGRP_STATUS_TS_GET( status ); + + + switch ( STATE ) { + case 0: + DWMAC_CORE_PRINT_DBG( "- TX (Stopped): Reset or Stop command\n" ); + break; + case 1: + DWMAC_CORE_PRINT_DBG( "- TX (Running):Fetching the Tx desc\n" ); + break; + case 2: + DWMAC_CORE_PRINT_DBG( "- TX (Running): Waiting for end of tx\n" ); + break; + case 3: + DWMAC_CORE_PRINT_DBG( "- TX (Running): Reading the data " + "and queuing the data into the Tx buf\n" ); + break; + case 6: + DWMAC_CORE_PRINT_DBG( "- TX (Suspended): Tx Buff Underflow " + "or an unavailable Transmit descriptor\n" ); + break; + case 7: + DWMAC_CORE_PRINT_DBG( "- TX (Running): Closing Tx descriptor\n" ); + break; + default: + break; + } +} + +static void show_rx_process_state( const uint32_t status ) +{ + const uint32_t STATE = DMAGRP_STATUS_RS_GET( status ); + + + switch ( STATE ) { + case 0: + DWMAC_CORE_PRINT_DBG( "- RX (Stopped): Reset or Stop command\n" ); + break; + case 1: + DWMAC_CORE_PRINT_DBG( "- RX (Running): Fetching the Rx desc\n" ); + break; + case 2: + DWMAC_CORE_PRINT_DBG( "- RX (Running):Checking for end of pkt\n" ); + break; + case 3: + DWMAC_CORE_PRINT_DBG( "- RX (Running): Waiting for Rx pkt\n" ); + break; + case 4: + DWMAC_CORE_PRINT_DBG( "- RX (Suspended): Unavailable Rx buf\n" ); + break; + case 5: + DWMAC_CORE_PRINT_DBG( "- RX (Running): Closing Rx descriptor\n" ); + break; + case 6: + DWMAC_CORE_PRINT_DBG( "- RX(Running): Flushing the current frame" + " from the Rx buf\n" ); + break; + case 7: + DWMAC_CORE_PRINT_DBG( "- RX (Running): Queuing the Rx frame" + " from the Rx buf into memory\n" ); + break; + default: + break; + } +} + +#else /* DWMAC_CORE_DEBUG */ + #define show_tx_process_state( status ) + #define show_rx_process_state( status ) +#endif /* DWMAC_CORE_DEBUG */ + +void dwmac_core_dma_interrupt( void *arg ) +{ + dwmac_common_context *self = (dwmac_common_context *) arg; + dwmac_common_dma_irq_counts *count = &self->stats.dma_irq_counts; + rtems_event_set events_receive = 0; + rtems_event_set events_transmit = 0; + + /* Get interrupt status */ + uint32_t irq_status = self->dmagrp->status & self->dmagrp->interrupt_enable; + uint32_t irq_handled = 0; + uint32_t irq_disable = 0; + + + DWMAC_CORE_PRINT_DBG( "%s: [CSR5: 0x%08x]\n", __func__, irq_status ); + + /* It displays the DMA process states (CSR5 register) if DWMAC_CORE_DEBUG is #defined */ + show_tx_process_state( self->dmagrp->status ); + show_rx_process_state( self->dmagrp->status ); + + /* Is there any abnormal interrupt? */ + if ( irq_status & DMAGRP_STATUS_AIS ) { + DWMAC_CORE_PRINT_DBG( "CSR5[15] DMA ABNORMAL IRQ: " ); + + if ( irq_status & DMAGRP_STATUS_UNF ) { + DWMAC_CORE_PRINT_DBG( "transmit underflow\n" ); + events_transmit |= DWMAC_COMMON_EVENT_TX_BUMP_UP_DMA_THRESHOLD; + irq_handled |= DMAGRP_STATUS_UNF; + irq_disable |= DMAGRP_INTERRUPT_ENABLE_UNE; + ++count->tx_underflow; + } + + if ( irq_status & DMAGRP_STATUS_TJT ) { + DWMAC_CORE_PRINT_DBG( "transmit jabber\n" ); + irq_handled |= DMAGRP_STATUS_TJT; + ++count->tx_jabber; + } + + if ( irq_status & DMAGRP_STATUS_OVF ) { + DWMAC_CORE_PRINT_DBG( "recv overflow\n" ); + irq_handled |= DMAGRP_STATUS_OVF; + ++count->rx_overflow; + } + + if ( irq_status & DMAGRP_STATUS_TU ) { + DWMAC_CORE_PRINT_DBG( "transmit buffer unavailable\n" ); + irq_handled |= DMAGRP_STATUS_TU; + ++count->tx_buf_unav; + } + + if ( irq_status & DMAGRP_STATUS_RU ) { + DWMAC_CORE_PRINT_DBG( "receive buffer unavailable\n" ); + irq_handled |= DMAGRP_STATUS_RU; + ++count->rx_buf_unav; + } + + if ( irq_status & DMAGRP_STATUS_RPS ) { + DWMAC_CORE_PRINT_DBG( "receive process stopped\n" ); + irq_handled |= DMAGRP_STATUS_RPS; + ++count->rx_process_stopped; + } + + if ( irq_status & DMAGRP_STATUS_RWT ) { + DWMAC_CORE_PRINT_DBG( "receive watchdog\n" ); + irq_handled |= DMAGRP_STATUS_RWT; + ++count->rx_watchdog; + } + + if ( irq_status & DMAGRP_STATUS_ETI ) { + DWMAC_CORE_PRINT_DBG( "transmit early interrupt\n" ); + irq_handled |= DMAGRP_STATUS_ETI; + ++count->tx_early; + } + + if ( irq_status & DMAGRP_STATUS_ERI ) { + DWMAC_CORE_PRINT_DBG( "receive early interrupt\n" ); + irq_handled |= DMAGRP_STATUS_ERI; + ++count->rx_early; + } + + if ( irq_status & DMAGRP_STATUS_TPS ) { + DWMAC_CORE_PRINT_DBG( "transmit process stopped\n" ); + events_transmit |= DWMAC_COMMON_EVENT_TASK_INIT; + irq_handled |= DMAGRP_STATUS_TPS; + irq_disable |= DMAGRP_INTERRUPT_ENABLE_TSE; + ++count->tx_process_stopped; + } + + if ( irq_status & DMAGRP_STATUS_FBI ) { + DWMAC_CORE_PRINT_DBG( "fatal bus error\n" ); + events_transmit |= DWMAC_COMMON_EVENT_TASK_INIT; + irq_handled |= DMAGRP_STATUS_FBI; + irq_disable |= DMAGRP_INTERRUPT_ENABLE_FBE; + ++count->fatal_bus_error; + } + + irq_handled |= DMAGRP_STATUS_AIS; + } + + /* Is there any normal interrupt? */ + if ( irq_status & DMAGRP_STATUS_NIS ) { + /* Transmit interrupt */ + if ( irq_status & DMAGRP_STATUS_TI ) { + events_transmit |= DWMAC_COMMON_EVENT_TX_FRAME_TRANSMITTED; + irq_handled |= DMAGRP_STATUS_TI; + irq_disable |= DMAGRP_INTERRUPT_ENABLE_TIE; + ++count->transmit; + } + + /* Receive interrupt */ + if ( irq_status & DMAGRP_STATUS_RI ) { + events_receive |= DWMAC_COMMON_EVENT_RX_FRAME_RECEIVED; + irq_handled |= DMAGRP_STATUS_RI; + irq_disable |= DMAGRP_INTERRUPT_ENABLE_RIE; + ++count->receive; + } + + irq_handled |= DMAGRP_STATUS_NIS; + } + + /* Optional hardware blocks, interrupts should be disabled */ + if ( irq_status + & ( DMAGRP_STATUS_GMI | DMAGRP_STATUS_GLI ) ) { + DWMAC_CORE_PRINT_DBG( "%s: unexpected status %08x\n", __func__, + irq_status ); + + if ( irq_status & DMAGRP_STATUS_GMI ) { + irq_handled |= DMAGRP_STATUS_GMI; + ++count->unhandled; + } + + if ( irq_status & DMAGRP_STATUS_GLI ) { + irq_handled |= DMAGRP_STATUS_GLI; + ++count->unhandled; + } + } + + /* Count remaining unhandled interrupts (there should not be any) */ + if ( ( irq_status & 0x1FFCF ) != irq_handled ) { + ++count->unhandled; + } + + /* Disable interrupts which need further handling by tasks. + * The tasks will re-enable them. */ + self->dmagrp->interrupt_enable &= ~irq_disable; + + /* Clear interrupts */ + self->dmagrp->status = irq_handled; + + /* Send events to receive task */ + if ( events_receive != 0 ) { + (void) rtems_bsdnet_event_send( self->task_id_rx, events_receive ); + } + + /* Send events to transmit task */ + if ( events_transmit != 0 ) { + (void) rtems_bsdnet_event_send( self->task_id_tx, events_transmit ); + } + + DWMAC_CORE_PRINT_DBG( "\n\n" ); +} + +void dwmac_core_dma_flush_tx_fifo( dwmac_common_context *self ) +{ + self->dmagrp->operation_mode |= DMAGRP_OPERATION_MODE_FTF; + + do { + } while ( ( self->dmagrp->operation_mode & DMAGRP_OPERATION_MODE_FTF ) != 0 ); +} + +void dwmac_core_set_mac_addr( + const uint8_t addr[6], + volatile uint32_t *reg_high, + volatile uint32_t *reg_low ) +{ + uint32_t data = MAC_HIGH_ADDRHI( ( addr[5] << 8 ) | addr[4] ); + + + /* For MAC Addr registers se have to set the Address Enable (AE) + * bit that has no effect on the High Reg 0 where the bit 31 (MO) + * is RO. + */ + data |= MAC_HIGH_AE; + *reg_high = data; + + data = + ( (uint32_t) addr[3] << 24 ) + | ( (uint32_t) addr[2] << 16 ) + | ( (uint32_t) addr[1] << 8 ) + | addr[0]; + *reg_low = data; +} + +/* Enable disable MAC RX/TX */ +void dwmac_core_set_mac( + dwmac_common_context *self, + const bool enable ) +{ + uint32_t value = self->macgrp->mac_configuration; + + + if ( enable ) { + value |= MACGRP_MAC_CONFIGURATION_RE | MACGRP_MAC_CONFIGURATION_TE; + } else { + value &= ~( MACGRP_MAC_CONFIGURATION_RE | MACGRP_MAC_CONFIGURATION_TE ); + } + + self->macgrp->mac_configuration = value; +} |