diff options
Diffstat (limited to 'c/src/libchip/network/dwmac-1000-core.c')
-rw-r--r-- | c/src/libchip/network/dwmac-1000-core.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/c/src/libchip/network/dwmac-1000-core.c b/c/src/libchip/network/dwmac-1000-core.c new file mode 100644 index 0000000000..770078af43 --- /dev/null +++ b/c/src/libchip/network/dwmac-1000-core.c @@ -0,0 +1,238 @@ +/** + * @file + * + * @brief DWMAC 1000 on-chip Ethernet controllers Core Handling + * + * Functions and data which are specific to the DWMAC 1000 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 <assert.h> +#include "dwmac-core.h" +#include "dwmac-common.h" + +#define DWMAC_1000_CORE_DEBUG + +//#undef DWMAC_1000_CORE_DEBUG +#ifdef DWMAC_1000_CORE_DEBUG +#define DWMAC_1000_CORE_PRINT_DBG( fmt, args ... ) printk( fmt, ## args ) +#else +#define DWMAC_1000_CORE_PRINT_DBG( fmt, args ... ) do { } while ( 0 ) +#endif + +#define DWMAC_1000_CORE_INIT \ + ( \ + ( MACGRP_MAC_CONFIGURATION_JD \ + | MACGRP_MAC_CONFIGURATION_BE ) \ + & ~MACGRP_MAC_CONFIGURATION_PS \ + ) + +#define DWMAC_1000_CORE_HASH_TABLE_SIZE 256 + +static volatile uint32_t *dwmac_1000_core_get_mac_addr_low( + dwmac_common_context *self, + const unsigned int mac_addr_index ) +{ + volatile uint32_t *addr = NULL; + + assert( self != NULL ); + assert( mac_addr_index <= 127 ); + + if ( mac_addr_index > 15 ) { + addr = &self->macgrp->mac_addr16_127[mac_addr_index].low; + } else { + addr = &self->macgrp->mac_addr0_15[mac_addr_index].low; + } + + return addr; +} + +static volatile uint32_t *dwmac_1000_core_get_mac_addr_high( + dwmac_common_context *self, + const unsigned int mac_addr_index ) +{ + volatile uint32_t *addr = NULL; + + assert( self != NULL ); + assert( mac_addr_index <= 127 ); + + if ( mac_addr_index > 15 ) { + addr = &self->macgrp->mac_addr16_127[mac_addr_index].high; + } else { + addr = &self->macgrp->mac_addr0_15[mac_addr_index].high; + } + + return addr; +} + +static void dwmac_1000_core_init( dwmac_common_context *self ) +{ + uint32_t value = self->macgrp->mac_configuration; + + value |= DWMAC_1000_CORE_INIT; + + if ( ( self->dmagrp->hw_feature & DMAGRP_HW_FEATURE_RXTYP1COE ) != 0 + || ( self->dmagrp->hw_feature & DMAGRP_HW_FEATURE_RXTYP2COE ) != 0 ) { + /* Enable RX checksum calculation offload to hardware */ + value |= MACGRP_MAC_CONFIGURATION_IPC; + } + + /* No Jumbo- or Giant frames. The network stack does not support them */ + value &= ~MACGRP_MAC_CONFIGURATION_JE; + value &= ~MACGRP_MAC_CONFIGURATION_TWOKPE; + self->macgrp->mac_configuration = value; + + /* Mask GMAC interrupts */ + self->macgrp->interrupt_mask = + MACGRP_INTERRUPT_MASK_RGSMIIIM + | MACGRP_INTERRUPT_MASK_PCSLCHGIM + | MACGRP_INTERRUPT_MASK_PCSANCIM + | MACGRP_INTERRUPT_MASK_TSIM; + + /* mask out interrupts because we don't handle them yet */ + self->macgrp->mmc_receive_interrupt_mask = ( uint32_t ) ~0L; + self->macgrp->mmc_transmit_interrupt_mask = ( uint32_t ) ~0L; + self->macgrp->mmc_ipc_receive_interrupt_mask = ( uint32_t ) ~0L; +} + +static void dwmac_1000_core_set_umac_addr( + dwmac_common_context *self, + const uint8_t *addr, + unsigned int reg_n ) +{ + dwmac_core_set_mac_addr( + addr, + dwmac_1000_core_get_mac_addr_high( self, reg_n ), + dwmac_1000_core_get_mac_addr_low( self, reg_n ) + ); +} + +static void dwmac_1000_core_set_hash_filter( + dwmac_common_context *self, + const bool add, + struct ifreq *ifr ) +{ + int eno = 0; + struct arpcom *ac = &self->arpcom; + + if ( add ) { + eno = ether_addmulti( ifr, ac ); + } else { + eno = ether_delmulti( ifr, ac ); + } + + if ( eno == ENETRESET ) { + struct ether_multistep step; + struct ether_multi *enm; + unsigned int num_multi = 0; + unsigned int index; + + ETHER_FIRST_MULTI( step, ac, enm ); + + while ( enm != NULL ) { + /* Find out how many multicast addresses we have to handle */ + uint64_t addrlo = 0; + uint64_t addrhi = 0; + + memcpy( &addrlo, enm->enm_addrlo, ETHER_ADDR_LEN ); + memcpy( &addrhi, enm->enm_addrhi, ETHER_ADDR_LEN ); + num_multi += 1U + (uint32_t) ( addrhi - addrlo ); + } + + if ( num_multi > DWMAC_1000_CORE_HASH_TABLE_SIZE ) { + /* Too many addresses to be hashed, Use the + * pass all multi option instead */ + + for ( index = 0; index < 8; ++index ) { + self->macgrp->hash_table_reg[index] = 0xffffffff; + } + + self->macgrp->mac_frame_filter |= MACGRP_MAC_FRAME_FILTER_PM; + } else if ( num_multi > 0 ) { + uint32_t hash_shadow[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + ETHER_FIRST_MULTI( step, ac, enm ); + + while ( enm != NULL ) { + uint64_t addrlo = 0; + uint64_t addrhi = 0; + + memcpy( &addrlo, enm->enm_addrlo, ETHER_ADDR_LEN ); + memcpy( &addrhi, enm->enm_addrhi, ETHER_ADDR_LEN ); + + while ( addrlo <= addrhi ) { + /* XXX: ether_crc32_le() does not work, why? */ + uint32_t crc = ether_crc32_be( (uint8_t *) &addrlo, ETHER_ADDR_LEN ); + + /* The upper 8 bits of the bit reversed 32 bit CRC are used for hash filtering. + * The most significant bits determine the register to be used and the + * least significant five bits determine which bit to be set within the register */ + uint32_t index_reg = ( crc >> 29 ) & 0x7; + uint32_t index_bit = ( crc >> 24 ) & 0x1f; + + hash_shadow[index_reg] |= 1U << index_bit; + ++addrlo; + } + + ETHER_NEXT_MULTI( step, enm ); + } + + for ( index = 0; index < 8; ++index ) { + self->macgrp->hash_table_reg[index] = hash_shadow[index]; + } + + /* Hash filter for multicast */ + self->macgrp->mac_frame_filter |= MACGRP_MAC_FRAME_FILTER_HMC; + } else { + /* Set all hash registers to accect to accept no multicast packets */ + for ( index = 0; index < 8; ++index ) { + self->macgrp->hash_table_reg[index] = 0x00000000; + } + + /* Hash filter for multicast */ + self->macgrp->mac_frame_filter |= MACGRP_MAC_FRAME_FILTER_HMC; + } + + DWMAC_1000_CORE_PRINT_DBG( + "Frame Filter reg: 0x%08x\n", + self->macgrp->mac_frame_filter + ); + DWMAC_1000_CORE_PRINT_DBG( + "Hash regs:\n" + "0x%08x\n" + "0x%08x\n" + "0x%08x\n" + "0x%08x\n" + "0x%08x\n" + "0x%08x\n" + "0x%08x\n" + "0x%08x\n", + self->macgrp->hash_table_reg[0], + self->macgrp->hash_table_reg[1], + self->macgrp->hash_table_reg[2], + self->macgrp->hash_table_reg[3], + self->macgrp->hash_table_reg[4], + self->macgrp->hash_table_reg[5], + self->macgrp->hash_table_reg[6], + self->macgrp->hash_table_reg[7] + ); + } +} + +const dwmac_common_core_ops dwmac_core_ops_1000 = { + .core_init = dwmac_1000_core_init, + .set_hash_filter = dwmac_1000_core_set_hash_filter, + .set_umac_addr = dwmac_1000_core_set_umac_addr, +}; |