summaryrefslogblamecommitdiffstats
path: root/c/src/libchip/network/dwmac-core.c
blob: 973441a511d6477211e77562d86150d45a175316 (plain) (tree)










































                                                                       




                                   





                                            



















                                                               
                                                                       



                                                                           




                                                                           




                                                                           





                                                                            
 
                                                                 



































































































































































































































































































































                                                                                          
/**
 * @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_FBE \
    | DMAGRP_INTERRUPT_ENABLE_UNE \
    | DMAGRP_INTERRUPT_ENABLE_AIE \
  )

#define DWMAC_CORE_INTR_ENABLE_ALL_MASK_TX \
  ( \
    DWMAC_CORE_INTR_ENABLE_DEFAULT_MASK_TX \
    | DMAGRP_INTERRUPT_ENABLE_TIE \
  )

#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_default( dwmac_common_context *self )
{
  self->dmagrp->interrupt_enable |= DWMAC_CORE_INTR_ENABLE_DEFAULT_MASK_TX;
}

void dwmac_core_enable_dma_irq_tx_transmitted( dwmac_common_context *self )
{
  self->dmagrp->interrupt_enable |= DMAGRP_INTERRUPT_ENABLE_TIE;
}

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_all( dwmac_common_context *self )
{
  self->dmagrp->interrupt_enable &= ~DWMAC_CORE_INTR_ENABLE_ALL_MASK_TX;
}

void dwmac_core_disable_dma_irq_tx_transmitted( dwmac_common_context *self )
{
  self->dmagrp->interrupt_enable &= ~DMAGRP_INTERRUPT_ENABLE_TIE;
}

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;
}