diff options
Diffstat (limited to 'cpsw/src/netif/cpswif.c')
-rwxr-xr-x | cpsw/src/netif/cpswif.c | 2999 |
1 files changed, 2999 insertions, 0 deletions
diff --git a/cpsw/src/netif/cpswif.c b/cpsw/src/netif/cpswif.c new file mode 100755 index 0000000..9ff45b0 --- /dev/null +++ b/cpsw/src/netif/cpswif.c @@ -0,0 +1,2999 @@ +/** + * @file - cpswif.c + * lwIP Ethernet interface for CPSW port + * + */ + +/** + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels <adam@sics.se> + * + */ + +/** + * Copyright (c) 2010 Texas Instruments Incorporated + * + * This file is dervied from the "ethernetif.c" skeleton Ethernet network + * interface driver for lwIP. + */ +#include <semaphore.h> +#include <bsp.h> +#include <sched.h> + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "netif/etharp.h" +#include "netif/ppp/pppoe.h" +#include "lwip/err.h" +#include "netif/cpswif.h" +#include "arch/cc.h" + +/* DriverLib Header Files required for this interface driver. */ +#include "cpsw.h" +#include "mdio.h" +#include "delay.h" +#include "phy.h" +#include "cache.h" + +#include "lwiplib.h" + +/* CPPI RAM size in bytes */ +#ifndef SIZE_CPPI_RAM +#define SIZE_CPPI_RAM 0x2000 +#endif + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) + +#define PORT_1 0x0 +#define PORT_2 0x1 +#define PORT_0_MASK 0x1 +#define PORT_1_MASK 0x2 +#define PORT_2_MASK 0x4 +#define HOST_PORT_MASK PORT_0_MASK +#define SLAVE_PORT_MASK(slv_port_num) (1 << slv_port_num) +#define PORT_MASK (0x7) +#define INDV_PORT_MASK(slv_port_num) (1 << slv_port_num) + +#define ENTRY_TYPE 0x30 +#define ENTRY_TYPE_IDX 7 +#define ENTRY_FREE 0 + +/* MDIO input and output frequencies in Hz */ +#define MDIO_FREQ_INPUT 125000000 +#define MDIO_FREQ_OUTPUT 1000000 + +#define CPDMA_BUF_DESC_OWNER 0x20000000 +#define CPDMA_BUF_DESC_SOP 0x80000000 +#define CPDMA_BUF_DESC_EOP 0x40000000 +#define CPDMA_BUF_DESC_EOQ 0x10000000 +#define CPDMA_BUF_DESC_FROM_PORT 0x70000 +#define CPDMA_BUF_DESC_FROM_PORT_SHIFT 16 +#define CPDMA_BUF_DESC_TO_PORT(port) ((port << 16) | 0x100000) +#define CPDMA_BD_LEN_MASK 0xFFFF +#define CPDMA_BD_PKTLEN_MASK 0xFFFF + +#define MAX_TRANSFER_UNIT 1500 +#define PBUF_LEN_MAX 1520 + +#define MIN_PKT_LEN 60 + +/* Define those to better describe the network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +#define MASK_LOWER_4BITS_BYTE (0x0F) +#define MASK UPPER_4BITS_BYTE (0xF0) + +#define MASK_BROADCAST_ADDR (0xFF) +#define MASK_MULTICAST_ADDR (0x01) + +#define ALE_ENTRY_VLAN 0x20 +#define ALE_ENTRY_VLANUCAST 0x30 +#define ALE_ENTRY_UCAST 0x10 +#define ALE_ENTRY_MCAST 0xD0 +#define ALE_ENTRY_OUI (0x80) +#define ALE_ENTRY_ADDR (0x10) +#define ALE_ENTRY_VLAN_ADDR (0x30) +#define ALE_VLAN_ENTRY_MEMBER_LIST 0 +#define ALE_VLAN_ENTRY_FRC_UNTAG_EGR 3 +#define ALE_VLAN_ENTRY_MCAST_UNREG (1) +#define ALE_VLAN_ENTRY_MCAST_REG (2) +#define ALE_VLAN_ENTRY_ID (3) +#define ALE_VLAN_ID_MASK (0x0FFF) +#define ALE_VLAN_ENTRY_ID_BIT0_BIT7 6 +#define ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11 7 +#define ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN (0x08) +#define ALE_VLANUCAST_ENTRY_ID_BIT0_BIT7 6 +#define ALE_VLANUCAST_ENTRY_TYPE_ID_BIT8_BIT11 7 +#define ALE_UCAST_ENTRY_TYPE 7 +#define ALE_UCAST_TYPE_MASK (0xC0) +#define ALE_UCAST_TYPE_SHIFT (6) +#define ALE_UCAST_TYPE_PERSISTANT (0x00) +#define ALE_UCAST_TYPE_UNTOUCHED (0x40) +#define ALE_UCAST_TYPE_OUI (0x80) +#define ALE_UCAST_TYPE_TOUCHED (0xC0) +#define ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC 8 +#define ALE_UCAST_ENTRY_DLR_BLK_SEC_MASK (0x03) +#define ALE_UCAST_ENTRY_PORT_SHIFT 2 +#define ALE_MCAST_ENTRY_TYPE_FWD_STATE 7 +#define ALE_MCAST_ENTRY_TYPE_FWD_STATE_SHIFT (6) +#define ALE_MCAST_ENTRY_PORTMASK_SUP 8 +#define ALE_MCAST_ENTRY_SUPER_MASK (0x02) +#define ALE_MCAST_ENTRY_SUPER_SHIFT (1) +#define ALE_MCAST_ENTRY_PORT_MASK (0x1C) +#define ALE_MCAST_ENTRY_PORTMASK_SHIFT 2 + +#define SELECT_10_HALF (1 << 0) +#define SELECT_10_FULL (1 << 1) +#define SELECT_100_HALF (1 << 2) +#define SELECT_100_FULL (1 << 3) +#define SELECT_1000_HALF (1 << 4) +#define SELECT_1000_FULL (1 << 5) + +#define SELECT_SPEED_10 (0) +#define SELECT_SPEED_100 (1) +#define SELECT_SPEED_1000 (2) + +#define SELECT_FORCED (0) +#define SELECT_AUTONEG (1) +#define SELECT_BOTH (2) + +#define SELECT_HALF_DUPLEX (0) +#define SELECT_FULL_DUPLEX (1) + + +#define SEM_DEFAULT_PSHARED (0) +#define SEM_INITIAL_VALUE (0) +#define RX_PRIORITY (0) +#define TX_PRIORITY (0) + +/** + * RX thread stack size. + */ +#define RX_THREAD_STACKSIZE 2048 + + /** + * RX thread stack size. + */ +#define TX_THREAD_STACKSIZE 2048 + +static void rx_thread_function(void* arg); +static void tx_thread_function(void* arg); + +/* TX Buffer descriptor data structure */ +struct cpdma_tx_bd { + volatile struct cpdma_tx_bd *next; + volatile u32_t bufptr; + volatile u32_t bufoff_len; + volatile u32_t flags_pktlen; + + /* helper to know which pbuf this tx bd corresponds to */ + volatile struct pbuf *pbuf; +}cpdma_tx_bd; + +/* RX Buffer descriptor data structure */ +struct cpdma_rx_bd { + volatile struct cpdma_rx_bd *next; + volatile u32_t bufptr; + volatile u32_t bufoff_len; + volatile u32_t flags_pktlen; + + /* helper to know which pbuf this rx bd corresponds to */ + volatile struct pbuf *pbuf; +}cpdma_rx_bd; + +/** + * Helper struct to hold the data used to operate on the receive + * buffer descriptor ring + */ +struct rxch { + /* The head of the bd chain which can be allocated for reception*/ + volatile struct cpdma_rx_bd *free_head; + + /* The head of the bd chain which is receiving data */ + volatile struct cpdma_rx_bd *recv_head; + + /* The tail of the bd chain which is receiving data */ + volatile struct cpdma_rx_bd *recv_tail; + + /* The number of free bd's, which can be allocated for reception */ + volatile u32_t free_num; +}rxch; + +/** + * Helper struct to hold the data used to operate on the transmit + * buffer descriptor ring + */ +struct txch { + /* The bd which is free to send */ + volatile struct cpdma_tx_bd *free_head; + + /* The head of the bd chain, being sent */ + volatile struct cpdma_tx_bd *send_head; + + /* The tail of the bd chain, being sent */ + volatile struct cpdma_tx_bd *send_tail; + + /* The number of free bd's, which can be sent */ + volatile u32_t free_num; +}txch; + +volatile struct cpdma_tx_bd *free_head; + +/** + * Slave port information + */ +struct cpswport{ + u32_t port_base; + u32_t sliver_base; + u32_t phy_addr; + + /* The PHY is capable of GitaBit or Not */ + u32_t phy_gbps; +}cpswport; + +/** + * CPSW instance information + */ +struct cpswinst{ + /* Base addresses */ + u32_t ss_base; + u32_t mdio_base; + u32_t wrpr_base; + u32_t ale_base; + u32_t cpdma_base; + u32_t cppi_ram_base; + u32_t host_port_base; + + /* Slave port information */ + struct cpswport port[MAX_SLAVEPORT_PER_INST]; + + /* The tx/rx channels for the interface */ + struct txch txch; + struct rxch rxch; + sys_thread_t RxThread; /**< RX receive thread data object pointer */ + sem_t rxsem; + sys_thread_t TxThread; /**< RX receive thread data object pointer */ + sem_t txsem; + sys_mutex_t txmtx; +}cpswinst; + +/* Defining set of CPSW base addresses for all the instances */ +static struct cpswinst cpsw_inst_data[MAX_CPSW_INST]; + +/** +* Function to setup the instance parameters inside the interface +* @param cpswif The interface structure pointer +* @return None. +*/ +static void +cpswif_inst_config(struct cpswportif *cpswif) { + u32_t inst_num = cpswif->inst_num; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + + /** + * Code is added for only instance 0. If more instances + * are there, assign base addresses and phy info here + */ + if(0 == inst_num) { + cpswinst->ss_base = CPSW0_SS_REGS; + cpswinst->mdio_base = CPSW0_MDIO_REGS; + cpswinst->wrpr_base = CPSW0_WR_REGS; + cpswinst->cpdma_base = CPSW0_CPDMA_REGS; + cpswinst->ale_base = CPSW0_ALE_REGS; + cpswinst->cppi_ram_base = CPSW0_CPPI_RAM_REGS; + cpswinst->host_port_base = CPSW0_PORT_0_REGS; + cpswinst->port[PORT_1].port_base = CPSW0_PORT_1_REGS; + cpswinst->port[PORT_1].sliver_base = CPSW0_SLIVER_1_REGS; +#ifdef CPSW0_PORT_1_PHY_ADDR + cpswinst->port[PORT_1].phy_addr = CPSW0_PORT_1_PHY_ADDR; + cpswinst->port[PORT_1].phy_gbps = CPSW0_PORT_1_PHY_GIGABIT; +#endif + cpswinst->port[PORT_2].port_base = CPSW0_PORT_2_REGS; + cpswinst->port[PORT_2].sliver_base = CPSW0_SLIVER_2_REGS; +#ifdef CPSW0_PORT_2_PHY_ADDR + cpswinst->port[PORT_2].phy_addr = CPSW0_PORT_2_PHY_ADDR; + cpswinst->port[PORT_2].phy_gbps = CPSW0_PORT_2_PHY_GIGABIT; +#endif + } +} + +/** + * Gives the index of the ALE entry which is free + * @param cpswinst The CPSW instance structure pointer + * + * @return index of the ALE entry which is free + * ERR_VAL if entry not found + */ +static err_t +cpswif_ale_entry_match_free(struct cpswinst *cpswinst) { + u32_t ale_entry[ALE_ENTRY_NUM_WORDS]; + s32_t idx; + + /* Check which ALE entry is free starting from 0th entry */ + for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) { + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + /* Break if the table entry is free */ + if (((*(((u8_t *)ale_entry) + ENTRY_TYPE_IDX)) + & ENTRY_TYPE) == ENTRY_FREE) { + return idx; + } + } + + return ERR_VAL; +} + +#ifdef CPSW_DUAL_MAC_MODE +/** + * Sets the VLAN and VLAN/UCAST entries in ALE table for Dual Mac mode + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param eth_addr Ethernet address + * + * @return None + */ +static void +cpswif_port_to_host_vlan_cfg(struct cpswinst *cpswinst, u32_t port_num, + u8_t *eth_addr) +{ + s32_t idx; + u32_t cnt; + u32_t ale_v_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + u32_t ale_vu_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_free(cpswinst); + + if(MAX_ALE_ENTRIES == idx) { + return; + } + + /* Set up the VLAN Entry */ + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) = + HOST_PORT_MASK | SLAVE_PORT_MASK(port_num); + + /** + * Set the bit fields for entry type and VLAN ID. Set the port + * number as VLAN ID. So only lsb 2 bits of VLAN_ID field will be used. + */ + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = port_num; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) = ALE_ENTRY_VLAN; + + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_FRC_UNTAG_EGR) = + HOST_PORT_MASK | SLAVE_PORT_MASK(port_num); + + /* Set the VLAN entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_v_entry); + + idx = cpswif_ale_entry_match_free(cpswinst); + + if(MAX_ALE_ENTRIES == idx) { + return; + } + + /* Set up the VLAN/Unicast Entry */ + for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_vu_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_vu_entry) + ALE_VLANUCAST_ENTRY_TYPE_ID_BIT8_BIT11) = + ALE_ENTRY_VLANUCAST; + *(((u8_t *)ale_vu_entry) + ALE_VLANUCAST_ENTRY_ID_BIT0_BIT7) = port_num; + + /* Set the VLAN/Unicast entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vu_entry); +} + +#else /* CPSW_DUAL_MAC_MODE */ + +/** + * Sets a unicast entry in the ALE table. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param eth_addr Ethernet address + * + * @return None + */ +static void +cpswif_ale_unicastentry_set(struct cpswinst *cpswinst, u32_t port_num, + u8_t *eth_addr) { + volatile u32_t cnt; + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ALE_ENTRY_UCAST; + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC) = + (port_num << ALE_UCAST_ENTRY_PORT_SHIFT); + + idx = cpswif_ale_entry_match_free(cpswinst); + + if (idx < MAX_ALE_ENTRIES ) { + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + } +} + +/** + * Sets a multicast entry in the ALE table + * @param cpswinst The CPSW instance structure pointer + * @param portmask The port mask for the port number + * @param eth_addr Ethernet Address + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static void +cpswif_ale_multicastentry_set(struct cpswinst *cpswinst, u32_t portmask, + u8_t *eth_addr) +{ + volatile u32_t cnt; + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_free(cpswinst); + if (idx < MAX_ALE_ENTRIES ) { + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ALE_ENTRY_MCAST; + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) |= + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + } +} +#endif /* CPSW_DUAL_MAC_MODE */ + +#ifdef CPSW_SWITCH_CONFIG +/** + * Checks the address is value for given type + * + * @param eth_addr Ethernet Address + * @param addr_type Type of address (Unicast, Multicast, Broadcast) + * + * @return the status + */ +static u32_t +check_valid_addr(u8_t *eth_addr, u32_t addr_type) { + u32_t cnt = 0; + + if (!addr_type) + return TRUE; + + for (cnt = 0; cnt < LEN_MAC_ADDRESS; cnt++) + if ((eth_addr[cnt] & MASK_BROADCAST_ADDR) != MASK_BROADCAST_ADDR) + break; + + if ((cnt == LEN_MAC_ADDRESS) && (addr_type == ADDR_TYPE_BROADCAST)) + return TRUE; + else if (cnt == LEN_MAC_ADDRESS) + return FALSE; + else if (addr_type == ADDR_TYPE_BROADCAST) + return FALSE; + + if (addr_type == ADDR_TYPE_MULTICAST) { + if ((eth_addr[0] & MASK_MULTICAST_ADDR) == MASK_MULTICAST_ADDR) + return TRUE; + } else if (addr_type == ADDR_TYPE_UNICAST) { + if ((eth_addr[0] & MASK_MULTICAST_ADDR) != MASK_MULTICAST_ADDR) + return TRUE; + } + + return FALSE; +} + +/** + * Gives the index of the ALE entry which is untouched ageable + * @param cpswinst The CPSW instance structure pointer + * + * @return index of the ALE entry which is free + * ERR_VAL if entry not found + */ +static err_t +cpswif_ale_entry_match_ageable(struct cpswinst *cpswinst) { + u32_t ale_entry[ALE_ENTRY_NUM_WORDS]; + u32_t type; + s32_t idx; + + /* Check which ALE entry is free starting from 0th entry */ + for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) { + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + type = *(((u8_t *)ale_entry) + ENTRY_TYPE_IDX) & ENTRY_TYPE; + + /* Goto next entry if the ale entry is not valid address */ + if ((type != ALE_ENTRY_ADDR) && (type != ALE_ENTRY_VLAN_ADDR)) + continue; + + if (check_valid_addr((u8_t *)ale_entry, ADDR_TYPE_MULTICAST)) + continue; + + type = *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) & ALE_UCAST_TYPE_MASK; + + if ((type != ALE_UCAST_TYPE_PERSISTANT) && (type != ALE_UCAST_TYPE_OUI)) + return idx; + } + + return ERR_VAL; +} + +/** + * Configure the rate limit for packets transmit of ports + * @param cpswinst The CPSW instance + * @param enable Enable Rate Limit + * @param direction Select TX or RX mode + * @param port_num Port Number + * @param addr_type Type of Address (Broadcast or Multicast) + * @param limit Vale of rate limit to be set + * + * @return None + * + **/ +static void +cpswif_rate_limit(struct cpswinst *cpswinst, u32_t enable, u32_t direction, + u32_t port_num, u32_t addr_type, u32_t limit) { + if (!enable) { + CPSWALERateLimitDisable(cpswinst->ale_base); + } + + if (addr_type == ADDR_TYPE_BROADCAST) { + CPSWALEBroadcastRateLimitSet(cpswinst->ale_base, port_num, limit); + } else if (addr_type == ADDR_TYPE_MULTICAST) { + CPSWALEMulticastRateLimitSet(cpswinst->ale_base, port_num, limit); + } + + if (port_num == 0) { + if (direction) { + CPSWALERateLimitTXMode(cpswinst->ale_base); + } else { + CPSWALERateLimitRXMode(cpswinst->ale_base); + } + } else { + if (direction) { + CPSWALERateLimitRXMode(cpswinst->ale_base); + } else { + CPSWALERateLimitTXMode(cpswinst->ale_base); + } + } + + CPSWALERateLimitEnable(cpswinst->ale_base); +} + +/** + * Gives the index of the ALE entry which match address of VLAN + * @param cpswinst The CPSW instance structure pointer + * @param eth_addr Ethernet address + * @param vid VLAN ID + * + * @return index of the ALE entry which match address of VLAN + * ERR_VAL if entry not found + */ +static err_t +cpswif_ale_entry_match_addr(struct cpswinst *cpswinst, u8_t *eth_addr, + u32_t vid) { + u32_t ale_entry[ALE_ENTRY_NUM_WORDS]; + u32_t type, cnt; + s32_t idx; + + /* Check which ALE entry is free starting from 0th entry */ + for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) { + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + type = *(((u8_t *)ale_entry) + ENTRY_TYPE_IDX) & ENTRY_TYPE; + + /* Goto next entry if the ale entry is not valid address */ + if ((type != ALE_ENTRY_ADDR) && (type != ALE_ENTRY_VLAN_ADDR)) + continue; + + if ((*(((u16_t *)ale_entry) + ALE_VLAN_ENTRY_ID) & ALE_VLAN_ID_MASK) != vid) + continue; + + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + if (*(((u8_t *)ale_entry) + cnt) != eth_addr[ETHARP_HWADDR_LEN - cnt -1]) + break; + + if (ETHARP_HWADDR_LEN == (cnt + 1)) + return idx; + } + } + + return ERR_VAL; +} + +/** + * Gives the index of the ALE entry which match vlan + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * + * @return index of the ALE entry which match vlan + * ERR_VAL if entry not found + */ +static err_t +cpswif_ale_entry_match_vlan(struct cpswinst *cpswinst, u32_t vid) { + u32_t ale_entry[ALE_ENTRY_NUM_WORDS]; + u32_t type; + s32_t idx; + + /* Check which ALE entry is free starting from 0th entry */ + for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) { + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + type = *(((u8_t *)ale_entry) + ENTRY_TYPE_IDX) & ENTRY_TYPE; + + /* Goto next entry if the ale entry is not valid vlan */ + if (type != ALE_ENTRY_VLAN) + continue; + + if ((*(((u16_t *)ale_entry) + ALE_VLAN_ENTRY_ID) & ALE_VLAN_ID_MASK) == vid) + return idx; + } + + return ERR_VAL; +} + +/** + * Adds an unicast entry in the ALE table. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param eth_addr Ethernet address + * @param flags Flags Settings + * 0x1 - Secure + * 0x2 - Block + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_unicastentry_add(struct cpswinst *cpswinst, u32_t port_num, + u8_t *eth_addr, u32_t flags, u32_t ucast_type) { + volatile u32_t cnt; + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_free(cpswinst); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ALE_ENTRY_ADDR | + (ucast_type << ALE_UCAST_TYPE_SHIFT); + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC) = + (port_num << ALE_UCAST_ENTRY_PORT_SHIFT) | + (flags & ALE_UCAST_ENTRY_DLR_BLK_SEC_MASK); + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Adds an OUI entry in the ALE table. + * @param cpswinst The CPSW instance structure pointer + * @param eth_addr Ethernet address + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_OUI_add(struct cpswinst *cpswinst, u8_t *eth_addr) { + volatile u32_t cnt; + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_free(cpswinst); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ALE_ENTRY_ADDR | ALE_ENTRY_OUI; + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Deletes an unicast entry in the ALE table. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param eth_addr Ethernet address + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_unicastentry_del(struct cpswinst *cpswinst, u32_t port_num, + u8_t *eth_addr) { + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ENTRY_FREE; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Adds a multicast entry in the ALE table + * @param cpswinst The CPSW instance structure pointer + * @param portmask The port mask for the port number + * @param eth_addr Ethernet Address + * @param super Supervisory Packet + * @param mcast_st Multicast Forward State + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_multicastentry_add(struct cpswinst *cpswinst, u32_t portmask, + u8_t *eth_addr, u32_t super, u32_t mcast_st) { + volatile s32_t idx; + volatile u32_t cnt; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) { + idx = cpswif_ale_entry_match_free(cpswinst); + } else { + /* Get the entry in the ALE table */ + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + } + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + portmask |= (*(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) & + ALE_MCAST_ENTRY_PORT_MASK); + + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = + (ALE_ENTRY_ADDR | + (mcast_st << ALE_MCAST_ENTRY_TYPE_FWD_STATE_SHIFT)); + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &= + ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) |= + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT) | + ((super << ALE_MCAST_ENTRY_SUPER_SHIFT) & + ALE_MCAST_ENTRY_SUPER_MASK); + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Deletes a multicast entry in the ALE table + * @param cpswinst The CPSW instance structure pointer + * @param portmask The port mask for the port number + * @param eth_addr Ethernet Address + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_multicastentry_del(struct cpswinst *cpswinst, u32_t portmask, + u8_t *eth_addr) { + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + if (portmask) { + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &= + ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) = + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + } else { + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ENTRY_FREE; + } + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Adds the VLAN entry in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * @param untag The force untagged egress + * @param reg_mcast The registered MCAST Flood Mask + * @param unreg_mcast The unregistered MCAST Flood Mask + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_vlan_add(struct cpswinst *cpswinst, u32_t vid, u32_t port_num, + u32_t untag, u32_t reg_mcast, u32_t unreg_mcast) { + s32_t idx; + u32_t ale_v_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_vlan(cpswinst, vid); + + if (ERR_VAL == idx) { + idx = cpswif_ale_entry_match_free(cpswinst); + } else { + /* Get the entry in the ALE table */ + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_v_entry); + } + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + /** + * Set the bit fields for entry type and VLAN ID. + */ + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = vid; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) = + ((vid >> ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN) & + MASK_LOWER_4BITS_BYTE) | ALE_ENTRY_VLAN; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_FRC_UNTAG_EGR) = untag; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MCAST_UNREG) = unreg_mcast; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MCAST_REG) = reg_mcast; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) |= + PORT_MASK & INDV_PORT_MASK(port_num); + + /* Set the VLAN entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_v_entry); + + return idx; +} + +/** + * Dlelete the VLAN entry in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_vlan_del(struct cpswinst *cpswinst, u32_t vid, u32_t port_num) { + s32_t idx; + u32_t mask; + u32_t ale_v_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_vlan(cpswinst, vid); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_v_entry); + + /* Set up the VLAN Entry */ + mask = *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) | PORT_MASK; + mask &= INDV_PORT_MASK(port_num); + + if (0 == mask) + return ERR_VAL; + + if (1 == mask) { + *(((u8_t *)ale_v_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ENTRY_FREE; + } else { + /* Set up the VLAN Entry */ + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) &= + ~INDV_PORT_MASK(port_num); + } + + /* Set the VLAN/Unicast entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_v_entry); + + return idx; +} + +/** + * Add the VLAN/UCAST entries in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * @param eth_addr Ethernet address + * @param flags Flags Settings + * 0x1 - Secure + * 0x2 - Block + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_vlan_add_ucast(struct cpswinst *cpswinst, u32_t vid, u32_t port_num, + u8_t *eth_addr, u32_t flags, u32_t ucast_type) { + s32_t idx; + u32_t cnt; + u32_t ale_vu_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_free(cpswinst); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + /** + * Set the bit fields for entry type and VLAN ID. + */ + *(((u8_t *)ale_vu_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = vid; + *(((u8_t *)ale_vu_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) = + ((vid >> ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN) & + MASK_LOWER_4BITS_BYTE) | ALE_ENTRY_VLAN_ADDR | + (ucast_type << ALE_UCAST_TYPE_SHIFT); + + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_vu_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_vu_entry) + ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC) = + (port_num << ALE_UCAST_ENTRY_PORT_SHIFT) | + (flags & ALE_UCAST_ENTRY_DLR_BLK_SEC_MASK); + + /* Set the VLAN entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vu_entry); + + return idx; +} + +/** + * Dlelete the VLAN/UCAST entry in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_vlan_del_ucast(struct cpswinst *cpswinst, u32_t vid, u32_t port_num, + u8_t *eth_addr) { + volatile s32_t idx; + u32_t ale_vu_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + *(((u8_t *)ale_vu_entry) + ALE_UCAST_ENTRY_TYPE) = ENTRY_FREE; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vu_entry); + + return idx; +} + +/** + * Add the VLAN/MCAST entries in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * @param eth_addr Ethernet address + * @param super Supervisory Packet + * @param mcast_st Multicast Forward State + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_vlan_add_mcast(struct cpswinst *cpswinst, u32_t vid, u32_t portmask, + u8_t *eth_addr, u32_t super, u32_t mcast_st) { + s32_t idx; + u32_t cnt; + u32_t ale_vm_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid); + + if (ERR_VAL == idx) { + idx = cpswif_ale_entry_match_free(cpswinst); + } else { + /* Get the entry in the ALE table */ + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_vm_entry); + } + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + /** + * Set the bit fields for entry type and VLAN ID. + */ + *(((u8_t *)ale_vm_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = vid; + *(((u8_t *)ale_vm_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) = + ((vid >> ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN) & + MASK_LOWER_4BITS_BYTE); + + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_vm_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + portmask |= (*(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) & + ALE_MCAST_ENTRY_PORT_MASK); + + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) |= + (ALE_ENTRY_VLAN_ADDR | + (mcast_st << ALE_MCAST_ENTRY_TYPE_FWD_STATE_SHIFT)); + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &= + ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) |= + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT) | + ((super << ALE_MCAST_ENTRY_SUPER_SHIFT) & + ALE_MCAST_ENTRY_SUPER_MASK); + + /* Set the VLAN entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vm_entry); + + return idx; +} + +/** + * Deletes a vlan multicast entry in the ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param portmask The port mask for the port number + * @param eth_addr Ethernet Address + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_vlan_del_mcast(struct cpswinst *cpswinst, u32_t vid, u32_t portmask, + u8_t *eth_addr) { + volatile s32_t idx; + u32_t ale_vm_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_vm_entry); + + if (portmask) { + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &= + ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) = + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + } else { + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ENTRY_FREE; + } + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vm_entry); + + return idx; +} + +#endif /* CPSW_SWITCH_CONFIG */ + +/** + * AutoNegotiates with phy for link, set it in silver and check for link status. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param adv Configuration for advertisement + * SELECT_10_HALF - 10Base Half Duplex + * SELECT_10_FULL - 10Base Full Duplex + * SELECT_100_HALF - 100Base Half Duplex + * SELECT_100_FULL - 100Base Full Duplex + * SELECT_1000_HALF - 1000Base Half Duplex + * SELECT_1000_FULL - 1000Base Full Duplex + * @return ERR_OK If link set up is successful + * others if not successful + */ +static err_t +cpswif_phy_autoneg(struct cpswinst *cpswinst, u32_t port_num, u32_t adv) { + err_t linkstat = ERR_CONN; + u16_t adv_val = 0, partnr_ablty = 0, gbps_partnr_ablty = 0, gig_adv_val = 0; + u32_t aut_neg_cnt = 200, auto_stat, transfer_mode = 0; + + /* Check if ethernet PHY is present or not */ + if (0 == (MDIOPhyAliveStatusGet(cpswinst->mdio_base) + & (1 << cpswinst->port[port_num - 1].phy_addr))) { + LWIP_PRINTF("\n\rNo PHY found at addr %d for Port %d of Instance %d.", + cpswinst->port[port_num - 1].phy_addr, + port_num, 0); + return linkstat; + } + + LWIP_PRINTF("\n\rPHY found at address %d for Port %d of Instance %d.", + cpswinst->port[port_num - 1].phy_addr, + port_num, 0); + + if (SELECT_1000_HALF == adv) { + LWIP_PRINTF("\n\rCPSW doesnot support Half Duplex for Gigabyte..."); + return linkstat; + } + + /* We advertise for 10/100 Mbps both half and full duplex */ + if (adv & SELECT_10_HALF) + adv_val |= PHY_10BT; + if (adv & SELECT_10_FULL) + adv_val |= PHY_10BT_FD; + if (adv & SELECT_100_HALF) + adv_val |= PHY_100BTX; + if (adv & SELECT_100_FULL) + adv_val |= PHY_100BTX_FD; + + gig_adv_val = 0; + partnr_ablty = TRUE; + gbps_partnr_ablty = FALSE; + + /** + * Not all the PHYs can operate at 1000 Mbps. So advertise only + * if the PHY is capable + */ + if (cpswinst->port[port_num -1].phy_gbps) { + LWIP_PRINTF("\n\rPhy supports Gigabyte..."); + if (adv & SELECT_1000_FULL) { + gig_adv_val = PHY_1000BT_FD; + partnr_ablty = TRUE; + gbps_partnr_ablty = TRUE; + } + if (adv & SELECT_1000_HALF) { + LWIP_PRINTF("\n\rCPSW doesnot support Half Duplex for Gigabyte..."); + } + } else { + LWIP_PRINTF("\n\rPhy doesnot support Gigabyte..."); + } + + LWIP_PRINTF("\n\rPerforming Auto-Negotiation..."); + + /** + * Now start Autonegotiation. PHY will talk to its partner + * and give us what the partner can handle + */ + if (PhyAutoNegotiate(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr, + &adv_val, &gig_adv_val) == TRUE) { + while (aut_neg_cnt) { + delay(50); + auto_stat = PhyAutoNegStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr); + if (TRUE == auto_stat) { + break; + } + aut_neg_cnt--; + } + + if (0 != aut_neg_cnt) { + linkstat = ERR_OK; + LWIP_PRINTF("\n\rAuto-Negotiation Successful."); + } else { + LWIP_PRINTF("\n\rAuto-Negotiation Not Successful."); + return ERR_CONN; + } + + /* Get what the partner supports */ + PhyPartnerAbilityGet(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr, + &partnr_ablty, &gbps_partnr_ablty); + if (gbps_partnr_ablty & PHY_LINK_PARTNER_1000BT_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 1000 Mbps."); + transfer_mode = CPSW_SLIVER_GIG_FULL_DUPLEX; + } else { + if ((adv_val & partnr_ablty) & PHY_100BTX_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Full Duplex."); + transfer_mode = CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_100BTX) { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Half Duplex."); + transfer_mode = CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_10BT_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Full Duplex."); + transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_10BT) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Half Duplex."); + transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } else { + LWIP_PRINTF("\n\rNo Valid Transfer Mode is detected."); + } + } + } else { + LWIP_PRINTF("\n\rAuto-Negotiation Not Successful."); + linkstat = ERR_CONN; + } + + /** + * Set the Sliver with the negotiation results if autonegotiation + * is successful + */ + if (linkstat == ERR_OK) { + CPSWSlTransferModeSet(cpswinst->port[port_num - 1].sliver_base, + transfer_mode); + } + + /* Check if PHY link is there or not */ + if (FALSE == ((PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr, 1000)))) { + LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Inst %d.", + port_num, 0); + return linkstat; + } + + LWIP_PRINTF("\n\rPHY link verified for Port %d of Instance %d.", + port_num, 0); + + CPSWSlRGMIIEnable( + cpswinst->port[port_num - 1].sliver_base); + + return linkstat; +} + +/** + * Manually configure phy, set it in silver and check for link status. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param speed Configuration for speed + * SELECT_SPEED_1000 - 1000 Mbps + * SELECT_SPEED_100 - 100 Mbps + * SELECT_SPEED_10 - 10 Mbps + * @param duplex Configuration for duplex + * SELECT_HALF_DUPLEX - Half Duplex + * SELECT_FULL_DUPLEX - Full Duplex + * @return ERR_OK If link set up is successful + * others if not successful + */ +static err_t +cpswif_phy_forced(struct cpswinst *cpswinst, u32_t port_num, u32_t speed, + u32_t duplex) { + err_t linkstat = ERR_CONN; + u16_t speed_val = 0, duplex_val = 0; + u32_t frc_stat_cnt = 200, frc_stat = FALSE, transfer_mode = 0; + + /* Check if ethernet PHY is present or not */ + if (0 == (MDIOPhyAliveStatusGet(cpswinst->mdio_base) + & (1 << cpswinst->port[port_num - 1].phy_addr))){ + LWIP_PRINTF("\n\rNo PHY found at addr %d for Port %d of Instance %d.", + cpswinst->port[port_num - 1].phy_addr, + port_num, 0); + return linkstat; + } + + LWIP_PRINTF("\n\rPHY found at address %d for Port %d of Instance %d.", + cpswinst->port[port_num - 1].phy_addr, + port_num, 0); + + /* configure control for speed and duples */ + if (SELECT_SPEED_1000 == speed) + speed_val = PHY_SPEED_1000MBPS; + else if (SELECT_SPEED_100 == speed) + speed_val = PHY_SPEED_100MBPS; + + if (TRUE == duplex) + duplex_val = PHY_FULL_DUPLEX; + + if (SELECT_SPEED_1000 == speed) { + LWIP_PRINTF("\n\rManual Configuration not allowed for Gigabyte..."); + return linkstat; + } + + if (FALSE == PhyReset(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr)) { + LWIP_PRINTF("\n\rPHY Reset Failed..."); + return linkstat; + } + + if (TRUE == (PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr, 1000))) { + while (frc_stat_cnt) { + delay(50); + /* Check if PHY link is there or not */ + frc_stat = (PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr, 1000)); + + if (TRUE == frc_stat) { + LWIP_PRINTF("\n\rPHY Link is Down."); + break; + } + frc_stat_cnt--; + } + } + + LWIP_PRINTF("\n\rPerforming Manual Configuration..."); + + frc_stat_cnt = 200; + frc_stat = FALSE; + + if (PhyConfigure(cpswinst->mdio_base, cpswinst->port[port_num -1].phy_addr, + speed_val, duplex_val)) { + while (frc_stat_cnt) { + delay(50); + frc_stat = PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr, 1000); + + if (1 == frc_stat) { + break; + } + frc_stat_cnt--; + } + + if (0 != frc_stat_cnt) { + linkstat = ERR_OK; + LWIP_PRINTF("\n\rPhy Configuration Successful."); + LWIP_PRINTF("\n\rPHY link verified for Port %d of Instance %d.", + port_num, 0); + } else { + LWIP_PRINTF("\n\rPhy Configuration Successful."); + LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Inst %d.", + port_num, 0); + return ERR_CONN; + } + + if (SELECT_SPEED_1000 == speed) { + LWIP_PRINTF("\n\rTransfer Mode : 1000 Mbps."); + transfer_mode = CPSW_SLIVER_GIG_FULL_DUPLEX; + } else { + if (SELECT_SPEED_10 == speed) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps "); + transfer_mode = CPSW_SLIVER_INBAND; + } else { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps "); + } + if (TRUE == duplex) { + LWIP_PRINTF("Full Duplex."); + transfer_mode |= CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else { + LWIP_PRINTF("Half Duplex."); + transfer_mode |= CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } + } + } else { + LWIP_PRINTF("\n\rPhy Configuration Not Successful."); + LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Inst %d.", + port_num, 0); + linkstat = ERR_CONN; + } + + /** + * Set the Sliver with the forced phy configuration + */ + CPSWSlTransferModeSet(cpswinst->port[port_num - 1].sliver_base, + transfer_mode); + + CPSWSlRGMIIEnable(cpswinst->port[port_num - 1].sliver_base); + + return linkstat; +} + +/** +* Function to setup the link. AutoNegotiates with the phy for link +* setup and set the CPSW with the result of autonegotiation. +* @param cpswportif The cpsw port interface structure pointer +* @return ERR_OK If link set up is successful +* others if not successful +*/ +static err_t +cpswif_autoneg_config(u32_t inst_num, u32_t port_num) { + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + err_t linkstat = ERR_CONN; + u16_t adv_val, partnr_ablty, gbps_partnr_ablty, gig_adv_val; + u32_t aut_neg_cnt = 200, auto_stat, transfer_mode = 0; + + /* We advertise for 10/100 Mbps both half and full duplex */ + adv_val = (PHY_100BTX | PHY_100BTX_FD | PHY_10BT | PHY_10BT_FD); + + /** + * Not all the PHYs can operate at 1000 Mbps. So advertise only + * if the PHY is capable + */ + if(TRUE == cpswinst->port[port_num -1].phy_gbps) { + gig_adv_val = PHY_1000BT_FD; + partnr_ablty = TRUE; + gbps_partnr_ablty = TRUE; + } else { + gig_adv_val = 0; + partnr_ablty = TRUE; + gbps_partnr_ablty = FALSE; + } + + LWIP_PRINTF("\n\rPerforming Auto-Negotiation..."); + + /** + * Now start Autonegotiation. PHY will talk to its partner + * and give us what the partner can handle + */ + if(PhyAutoNegotiate(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr, + &adv_val, &gig_adv_val) == TRUE) { + while(aut_neg_cnt) { + delay(50); + auto_stat = PhyAutoNegStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr); + if(TRUE == auto_stat) { + break; + } + aut_neg_cnt--; + } + + if(0 != aut_neg_cnt) { + linkstat = ERR_OK; + LWIP_PRINTF("\n\rAuto-Negotiation Successful."); + } else { + LWIP_PRINTF("\n\rAuto-Negotiation Not Successful."); + return ERR_CONN; + } + + /* Get what the partner supports */ + PhyPartnerAbilityGet(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr, + &partnr_ablty, &gbps_partnr_ablty); + if(gbps_partnr_ablty & PHY_LINK_PARTNER_1000BT_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 1000 Mbps."); + transfer_mode = CPSW_SLIVER_GIG_FULL_DUPLEX; + } else { + if ((adv_val & partnr_ablty) & PHY_100BTX_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Full Duplex."); + transfer_mode = CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_100BTX) { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Half Duplex."); + transfer_mode = CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_10BT_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Full Duplex."); + transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_10BT) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Half Duplex."); + transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } else { + LWIP_PRINTF("\n\rNo Valid Transfer Mode is detected."); + } + } + } else { + LWIP_PRINTF("\n\rAuto-Negotiation Not Successful."); + linkstat = ERR_CONN; + } + + /** + * Set the Sliver with the negotiation results if autonegotiation + * is successful + */ + if(linkstat == ERR_OK) { + CPSWSlTransferModeSet(cpswinst->port[port_num - 1].sliver_base, + transfer_mode); + } + + return linkstat; +} + +/** + * This function allocates the rx buffer descriptors ring. The function + * internally calls pbuf_alloc() and allocates the pbufs to the rx buffer + * descriptors. + * + * @param cpswinst The CPSW instance structure pointer + * @return None + */ +static void +cpswif_rxbd_alloc(struct cpswinst *cpswinst) { + struct rxch *rxch = &(cpswinst->rxch); + struct pbuf *p; + volatile struct cpdma_rx_bd *curr_bd, *last_bd, *recv_tail, *recv_head; + u32_t saved_free_num; + + /* Start from the free head of the chain */ + curr_bd = rxch->free_head; + + /* Note down the current positions */ + recv_head = rxch->free_head; + recv_tail = rxch->recv_tail; + saved_free_num = rxch->free_num; + last_bd = rxch->recv_tail; + + while(rxch->free_num) { + /** + * Try to get a pbuf of max. length. This shall be cache line aligned if + * cache is enabled. + */ + p = pbuf_alloc(PBUF_RAW, PBUF_LEN_MAX, PBUF_POOL); + + /** + * Allocate bd's if p is not NULL. This allocation doesnt support + * pbuf chaining. + */ + if(p != NULL) { +#ifdef LWIP_CACHE_ENABLED + /** + * Clean the pbuf structure info. This is needed to prevent losing + * pbuf structure info when we invalidate the pbuf on rx interrupt + */ + CacheDataCleanBuff((u32_t)(p), (u32_t)(SIZEOF_STRUCT_PBUF)); +#endif + curr_bd->bufptr = (u32_t)(p->payload); + curr_bd->bufoff_len = p->len; + curr_bd->flags_pktlen = CPDMA_BUF_DESC_OWNER; + + /* Save the pbuf */ + curr_bd->pbuf = p; + last_bd = curr_bd; + curr_bd = curr_bd->next; + rxch->free_num--; + } else { + break; + } + } + + if(saved_free_num == rxch->free_num) { + /* No bd's were allocated. Go back. */ + return; + } + + rxch->recv_tail = last_bd; + + /** + * If the entire ring is traversed, curr_bd will be NULL. In that case, + * write the Rx HDP in order to continue reception + */ + if(NULL != curr_bd) { + rxch->free_head = curr_bd; + } else { + CPSWCPDMARxHdrDescPtrWrite(cpswinst->cpdma_base, (u32_t)(recv_head), 0); + } + + recv_tail->next = recv_head; + + /* Close the ring to prevent overwriting of pbuf data */ + last_bd->next = NULL; + + /** + * Check if the reception has ended. If the EOQ flag is set, the NULL + * Pointer is already taken by the DMA engine. So we need to write the + * RX HDP with the next descriptor. + */ + if(recv_tail->flags_pktlen & CPDMA_BUF_DESC_EOQ) { + CPSWCPDMARxHdrDescPtrWrite(cpswinst->cpdma_base, (u32_t)(recv_head), 0); + } +} + +/** + * This function does the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf might be + * chained. That is, one pbuf can span more than one tx buffer descriptors + * + * @param netif the network interface state for this ethernetif + * @param pbuf the pbuf which is to be sent over EMAC + * @return status ERR_OK, if transmit was successful + * ERR_MEM, if no memory available + */ +static err_t +cpswif_transmit(struct netif *netif, struct pbuf *pbuf) { + struct pbuf *q; + struct txch *txch; + volatile struct cpdma_tx_bd *curr_bd, *bd_to_send, *bd_end; + struct cpswportif *cpswif = netif->state; + u32_t inst_num = cpswif->inst_num; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + +#ifdef CPSW_DUAL_MAC_MODE + u32_t port_num = cpswif->port_num; +#endif + + txch = &(cpswinst->txch); + + /* Do not send if there are no enough free bd's */ + if(pbuf_clen(pbuf) > txch->free_num) { + return ERR_MEM; + } + + /* Get the buffer descriptor which is free to transmit */ + curr_bd = txch->free_head; + + bd_to_send = txch->free_head; + + /* Update the total packet length */ + curr_bd->flags_pktlen = pbuf->tot_len; + + /* Indicate the start of the packet */ + curr_bd->flags_pktlen |= (CPDMA_BUF_DESC_SOP | CPDMA_BUF_DESC_OWNER); + +#ifdef CPSW_DUAL_MAC_MODE + /* Indicate to which port the packet has to be sent */ + curr_bd->flags_pktlen |= CPDMA_BUF_DESC_TO_PORT(port_num); +#endif + + /* Copy pbuf information into TX buffer descriptors */ + for(q = pbuf; q != NULL; q = q->next) { +#ifdef LWIP_CACHE_ENABLED + /** + * Make sure that the payload is written to memory. Clean + * the portion of cache to make it coherent with the memory. + */ + CacheDataCleanBuff((u32_t)(q->payload), (u32_t)(q->len)); +#endif + /* Intialize the buffer pointer and length */ + curr_bd->bufptr = (u32_t)(q->payload); + curr_bd->bufoff_len = (q->len) & CPDMA_BD_LEN_MASK; + bd_end = curr_bd; + curr_bd->pbuf = pbuf; + curr_bd = curr_bd->next; + + /* Decrement free bds, since one is consumed */ + txch->free_num--; + } + /* Indicate the end of the packet */ + bd_end->next = NULL; + bd_end->flags_pktlen |= CPDMA_BUF_DESC_EOP; + bd_end->flags_pktlen &= ~CPDMA_BUF_DESC_EOQ; + + txch->free_head = curr_bd; + + /* For the first time, write the HDP with the filled bd */ + if(txch->send_tail == NULL) { + CPSWCPDMATxHdrDescPtrWrite(cpswinst->cpdma_base, + (u32_t)(bd_to_send), 0); + } else { + /** + * Chain the bd's. If the DMA engine, already reached the end of the chain, + * the EOQ will be set. In that case, the HDP shall be written again. + */ + curr_bd = txch->send_tail; + curr_bd->next = bd_to_send; + + if(curr_bd->flags_pktlen & CPDMA_BUF_DESC_EOQ) { + /* Write the Header Descriptor Pointer and start DMA */ + CPSWCPDMATxHdrDescPtrWrite(cpswinst->cpdma_base, + (u32_t)(bd_to_send), 0); + } + } + + txch->send_tail = bd_end; + + return ERR_OK; +} + +/** + * This function will send a packet through the emac if the channel is + * available. Otherwise, the packet will be queued in a pbuf queue. + * + * @param netif The lwip network interface structure for this ethernetif + * @param p The MAC packet to send (e.g. IP packet including + * MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + */ +static err_t +cpswif_output(struct netif *netif, struct pbuf *p) { + err_t stat; + struct pbuf *q; + struct cpswportif *cpswif = netif->state; + u32_t inst_num = cpswif->inst_num; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + SYS_ARCH_DECL_PROTECT(lev); + + /** + * This entire function must run within a "critical section" to preserve + * the integrity of the transmit pbuf queue. + */ + + sys_mutex_lock(&cpswinst->txmtx); + SYS_ARCH_PROTECT(lev); + + /** + * Adjust the packet length if less than minimum required. + */ + if(p->tot_len < MIN_PKT_LEN) { + p->tot_len = MIN_PKT_LEN; + + for(q = p; q->next != NULL; q = q->next) { + q->next->tot_len = q->tot_len - q->len; + } + + /* Adjust the length of the last pbuf. (contents - don't care) */ + q->len = q->tot_len; + } + + /** + * Bump the reference count on the pbuf to prevent it from being + * freed till we are done with it. + */ + pbuf_ref(p); + + /* call the actual transmit function */ + stat = cpswif_transmit(netif, p); + + /* Return to prior interrupt state and return. */ + SYS_ARCH_UNPROTECT(lev); + sys_mutex_unlock(&cpswinst->txmtx); + + return stat; +} + +/** + * Configures PHY link for a port + * @param cpswif The CPSW interface structure pointer + * @param slv_port_num The slave port number + * + * @return ERR_OK if link configurations are successful + * an error status if failed + */ +static err_t +cpswif_phylink_config(struct cpswportif * cpswif, u32_t slv_port_num) { + struct cpswinst *cpswinst = &cpsw_inst_data[cpswif->inst_num]; + err_t err; + + /* Check if ethernet PHY is present or not */ + if(0 == (MDIOPhyAliveStatusGet(cpswinst->mdio_base) + & (1 << cpswinst->port[slv_port_num - 1].phy_addr))){ + LWIP_PRINTF("\n\rNo PHY found at address %d for Port %d of Instance %d.", + cpswinst->port[slv_port_num - 1].phy_addr, slv_port_num, + cpswif->inst_num); + return ERR_CONN; + } + + LWIP_PRINTF("\n\rPHY found at address %d for Port %d of Instance %d.", + cpswinst->port[slv_port_num - 1].phy_addr, slv_port_num, + cpswif->inst_num); + + /** + * PHY is alive. So autonegotiate and get the speed and duplex + * parameters, set it in the sliver + */ + err = (err_t)(cpswif_autoneg_config(cpswif->inst_num, slv_port_num)); + + /* Check if PHY link is there or not */ + if(FALSE == ((PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[slv_port_num - 1].phy_addr, 1000)))) { + LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Instance %d.", + slv_port_num, cpswif->inst_num); + return ERR_CONN; + } + + LWIP_PRINTF("\n\rPHY link verified for Port %d of Instance %d.", + slv_port_num, cpswif->inst_num); + + CPSWSlRGMIIEnable(cpswinst->port[slv_port_num - 1].sliver_base); + + return err; +} + +/** + * Initializes the CPSW port + * @param netif The cpsw interface + * + * @return ERR_OK if port initialization is successful + * an error status if failed + */ +static err_t +cpswif_port_init(struct netif *netif) { + struct cpswportif *cpswif = (struct cpswportif*)(netif->state); + u32_t temp; + err_t err; + +#ifdef CPSW_DUAL_MAC_MODE + struct cpswinst *cpswinst = &cpsw_inst_data[cpswif->inst_num]; + u32_t curr_port = cpswif->port_num; +#endif + sem_init(&cpswinst->rxsem, SEM_DEFAULT_PSHARED, SEM_INITIAL_VALUE); + + cpswinst->RxThread = sys_thread_new("receive_thread", rx_thread_function, netif, RX_THREAD_STACKSIZE, RX_PRIORITY); + LWIP_ASSERT("RxThread creation error", (cpswinst->RxThread)); + + sem_init(&cpswinst->txsem, SEM_DEFAULT_PSHARED, SEM_INITIAL_VALUE); + + cpswinst->TxThread = sys_thread_new("transmit_thread", tx_thread_function, netif, TX_THREAD_STACKSIZE, TX_PRIORITY); + LWIP_ASSERT("TxThread creation error", (cpswinst->TxThread)); + + err = sys_mutex_new(&cpswinst->txmtx); + LWIP_ASSERT("TXLockMutex creation error", (err == ERR_OK)); + + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + for(temp = 0; temp < ETHARP_HWADDR_LEN; temp++) { + netif->hwaddr[temp] = cpswif->eth_addr[temp]; + } + + /* maximum transfer unit */ + netif->mtu = MAX_TRANSFER_UNIT; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + +#ifdef CPSW_DUAL_MAC_MODE + /* Set the ethernet address for the port */ + CPSWPortSrcAddrSet(cpswinst->port[curr_port - 1].port_base, + (u8_t *)(&(cpswif->eth_addr))); + + /** + * For Dual Mac mode, configure port0 and port1 for one VLAN ID; + * port0 and port2 for a different VLAN ID. Here we choose the + * port number as VLAN ID. + */ + CPSWPortVLANConfig(cpswinst->port[curr_port - 1].port_base, curr_port, 0, 0); + + cpswif_port_to_host_vlan_cfg(cpswinst, curr_port, + (u8_t *)(&(cpswif->eth_addr))); + + err = cpswif_phylink_config(cpswif, curr_port); + +#else + err = cpswif_phylink_config(cpswif, 1); + err = err & (cpswif_phylink_config(cpswif, 2)); + +#endif + + return err; +} + +/** + * This function intializes the CPDMA. + * The CPPI RAM will be initialized for transmit and receive + * buffer descriptor rings. + * + * @param cpswinst The CPSW instance structure pointer + * @return None + */ +static void +cpswif_cpdma_init(struct cpswinst *cpswinst) { + u32_t num_bd; + volatile struct cpdma_tx_bd *curr_txbd, *last_txbd; + volatile struct cpdma_rx_bd *curr_rxbd, *last_rxbd; + struct txch *txch; + struct rxch *rxch; + + txch = &(cpswinst->txch); + + /* Initialize the CPDMA memory. Only Channel 0 is supported */ + txch->free_head = (volatile struct cpdma_tx_bd*)(cpswinst->cppi_ram_base); + txch->send_head = txch->free_head; + txch->send_tail = NULL; + + /* Allocate half of the CPPI RAM for TX buffer descriptors */ + num_bd = (SIZE_CPPI_RAM >> 1) / sizeof(cpdma_tx_bd); + + /* All buffer descriptors are free to send */ + txch->free_num = num_bd; + + curr_txbd = txch->free_head; + + /* Initialize all the TX buffer descriptors ring */ + while(num_bd) { + curr_txbd->next = curr_txbd + 1; + curr_txbd->flags_pktlen = 0; + last_txbd = curr_txbd; + curr_txbd = curr_txbd->next; + num_bd--; + } + last_txbd->next = txch->free_head; + + /* Initialize the descriptors for the RX channel */ + rxch = &(cpswinst->rxch); + rxch->free_head = (volatile struct cpdma_rx_bd*)(cpswinst->cppi_ram_base + + (SIZE_CPPI_RAM >> 1)); + + /* Allocate half of the CPPI RAM available for RX buffer dscriptors */ + num_bd = (SIZE_CPPI_RAM >> 1) / sizeof(cpdma_rx_bd); + rxch->free_num = num_bd; + + curr_rxbd = rxch->free_head; + + /* Create the rx ring of buffer descriptors */ + while(num_bd) { + curr_rxbd->next = curr_rxbd + 1; + curr_rxbd->flags_pktlen = CPDMA_BUF_DESC_OWNER; + last_rxbd = curr_rxbd; + curr_rxbd = curr_rxbd->next; + num_bd--; + } + + last_rxbd->next = rxch->free_head; + + /* We are going to receive starting from the free head */ + rxch->recv_head = rxch->free_head; + rxch->recv_tail = last_rxbd; + cpswif_rxbd_alloc(cpswinst); + + /* close the ring */ + last_rxbd->next = NULL; + + CPSWCPDMARxHdrDescPtrWrite(cpswinst->cpdma_base, (u32_t)(rxch->recv_head), 0); +} + +/** + * In this function, the hardware should be initialized. + * Called from cpswif_init(). + * + * @param cpswportif The CPSW port interface structure pointer + * @return None + */ +static void +cpswif_inst_init(struct cpswportif *cpswif){ + u32_t inst_num = cpswif->inst_num; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + + /* Reset the different modules */ + CPSWSSReset(cpswinst->ss_base); + CPSWWrReset(cpswinst->wrpr_base); + CPSWSlReset(cpswinst->port[PORT_1].sliver_base); + CPSWSlReset(cpswinst->port[PORT_2].sliver_base); + CPSWCPDMAReset(cpswinst->cpdma_base); + + /* Initialize MDIO */ + MDIOInit(cpswinst->mdio_base, MDIO_FREQ_INPUT, MDIO_FREQ_OUTPUT); + delay(1); + + CPSWALEInit(cpswinst->ale_base); + + /* Set the port 0, 1 and 2 states to FORWARD */ + CPSWALEPortStateSet(cpswinst->ale_base, 0, CPSW_ALE_PORT_STATE_FWD); + CPSWALEPortStateSet(cpswinst->ale_base, 1, CPSW_ALE_PORT_STATE_FWD); + CPSWALEPortStateSet(cpswinst->ale_base, 2, CPSW_ALE_PORT_STATE_FWD); + +#ifdef CPSW_DUAL_MAC_MODE + /* For Dual Mac Mode, Configure for VLAN Aware Mode */ + CPSWALEVLANAwareSet(cpswinst->ale_base); + CPSWHostPortDualMacModeSet(cpswinst->host_port_base); + +#else /*CPSW_DUAL_MAC_MODE */ + /* For normal CPSW switch mode, set multicast entry. */ + u8_t bcast_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + cpswif_ale_multicastentry_set(cpswinst, + PORT_0_MASK | PORT_1_MASK | PORT_2_MASK, + bcast_addr); + cpswif_ale_unicastentry_set(cpswinst, 0, + (u8_t *)(&(cpswif->eth_addr))); + + /* Set the ethernet address for both the ports */ + CPSWPortSrcAddrSet(cpswinst->port[0].port_base, + (u8_t *)(&(cpswif->eth_addr))); + CPSWPortSrcAddrSet(cpswinst->port[1].port_base, + (u8_t *)(&(cpswif->eth_addr))); + +#endif /*CPSW_DUAL_MAC_MODE */ + + /* Enable the statistics. Lets see in case we come across any issues */ + CPSWStatisticsEnable(cpswinst->ss_base); + + /* Initialize the buffer descriptors for CPDMA */ + cpswif_cpdma_init(cpswinst); + + /* Acknowledge receive and transmit interrupts for proper interrupt pulsing*/ + CPSWCPDMAEndOfIntVectorWrite(cpswinst->cpdma_base, CPSW_EOI_TX_PULSE); + CPSWCPDMAEndOfIntVectorWrite(cpswinst->cpdma_base, CPSW_EOI_RX_PULSE); + + /* Enable the transmission and reception */ + CPSWCPDMATxEnable(cpswinst->cpdma_base); + CPSWCPDMARxEnable(cpswinst->cpdma_base); + + /* Enable the interrupts for channel 0 and for control core 0 */ + CPSWCPDMATxIntEnable(cpswinst->cpdma_base, 0); + CPSWWrCoreIntEnable(cpswinst->wrpr_base, 0, 0, CPSW_CORE_INT_TX_PULSE); + + CPSWCPDMARxIntEnable(cpswinst->cpdma_base, 0); + CPSWWrCoreIntEnable(cpswinst->wrpr_base, 0, 0, CPSW_CORE_INT_RX_PULSE); +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the functions cpswif_inst_init() and + * cpswif_port_init() to do low level initializations + * + * @param netif The lwip network interface structure for this ethernetif + * @return ERR_OK If the interface is initialized + * any other err_t on error + */ +err_t +cpswif_init(struct netif *netif) +{ + struct cpswportif *cpswif = (struct cpswportif*)(netif->state); + static u32_t inst_init_flag = 0; + u32_t inst_num = cpswif->inst_num; + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /** + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 10000000); + + /* let us use the interface number to identify netif */ +#ifdef CPSW_DUAL_MAC_MODE + netif->num = (u8_t)(((cpswif->inst_num * MAX_SLAVEPORT_PER_INST) + + cpswif->port_num - 1) & 0xFF); +#else + netif->num = (u8_t)(cpswif->inst_num); +#endif + + /** + * We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) + */ + netif->output = etharp_output; + netif->linkoutput = cpswif_output; + + /** + * Initialize an instance only once. Port initialization will be + * done separately. + */ + if(((inst_init_flag >> inst_num) & 0x01) == 0) { + cpswif_inst_config(cpswif); + cpswif_inst_init(cpswif); + inst_init_flag |= (1 << inst_num); + } + + if(cpswif_port_init(netif) != ERR_OK) { + return ERR_CONN; + } + + return ERR_OK; +} + +/** + * Handler for Receive interrupt. Packet processing is done in this + * interrupt handler itself. + * + * @param inst_num the instance for which interrupt was generated + * @param netif_arr the address of the array of netifs + * @return none + */ +void +cpswif_rx_inthandler(const u32_t inst_num) { + struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num]; + sem_t* const rxsem = &cpswinst->rxsem; + const u32_t cpdma_base = cpswinst->cpdma_base; + unsigned int curr_bd; + + sem_post(rxsem); + + /* Get the bd which contains the earliest filled data */ + curr_bd = CPSWCPDMARxCPRead(cpdma_base, 0); + /* Acknowledge that this packet is processed */ + CPSWCPDMARxCPWrite(cpdma_base, 0, (unsigned int)curr_bd); + CPSWCPDMAEndOfIntVectorWrite(cpdma_base, CPSW_EOI_RX_PULSE); +} + +static void +process_input(const u32_t inst_num, struct netif *const netif_arr) { + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + struct rxch *rxch; + volatile struct cpdma_rx_bd *curr_bd; + volatile struct pbuf *pbuf; + u32_t tot_len, if_num; + +#ifdef CPSW_DUAL_MAC_MODE + u32_t from_port; +#endif + + /* Get the rx channel pointer */ + rxch = &(cpswinst->rxch); + + /* Get the bd which contains the earliest filled data */ + curr_bd = rxch->recv_head; + + /** + * Process the receive buffer descriptors. When the DMA completes + * reception, OWNERSHIP flag will be cleared. + */ + while((curr_bd->flags_pktlen & CPDMA_BUF_DESC_OWNER) + != CPDMA_BUF_DESC_OWNER) { + +#ifdef CPSW_DUAL_MAC_MODE + /** + * From which slave port the packet came from ? + * We will use this to decide to which netif the packet + * is to be forwarded to. + */ + from_port = ((curr_bd->flags_pktlen) & CPDMA_BUF_DESC_FROM_PORT) + >> CPDMA_BUF_DESC_FROM_PORT_SHIFT; +#endif + + /* Get the total length of the packet */ + tot_len = (curr_bd->flags_pktlen) & CPDMA_BD_PKTLEN_MASK; + + /* Get the pbuf which is associated with the current bd */ + pbuf = curr_bd->pbuf; +#ifdef LWIP_CACHE_ENABLED + /** + * Invalidate the cache lines of the pbuf including payload. Because + * the memory contents got changed by DMA. + */ + CacheDataInvalidateBuff((u32_t)pbuf, (PBUF_LEN_MAX + SIZEOF_STRUCT_PBUF)); +#endif + + /* Update the len and tot_len fields for the pbuf in the chain */ + pbuf->len = (u16_t)(tot_len); + pbuf->tot_len = (u16_t)(tot_len); + + curr_bd->flags_pktlen = CPDMA_BUF_DESC_OWNER; + + /* Adjust the link statistics */ + LINK_STATS_INC(link.recv); + +#ifdef CPSW_DUAL_MAC_MODE + if_num = (inst_num * MAX_SLAVEPORT_PER_INST) + from_port - 1; +#else + if_num = inst_num; +#endif + struct netif * netif = netif_arr + if_num; + /* Process the packet */ + if(netif->input((struct pbuf *)pbuf, netif) != ERR_OK) { + /* Adjust the link statistics */ + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + curr_bd = curr_bd->next; + + /* One more buffer descriptor is free now */ + rxch->free_num++; + + /** + * If the DMA engine took the NULL pointer, we dont have any bd to + * process until new bd's are allocated. + */ + if(curr_bd == NULL) { + rxch->recv_head = rxch->free_head; + break; + } + rxch->recv_head = curr_bd; + } + + /* We got some bd's freed; Allocate them */ + cpswif_rxbd_alloc(cpswinst); +} + +static void +rx_thread_function(void* arg) { +struct netif *const netif = arg; +struct cpswportif * const cpswif = netif->state; +const u32_t inst_num = cpswif->inst_num; +struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num]; +sem_t *const rxsem = &cpswinst->rxsem; +while (1) { + sem_wait(rxsem); + process_input(inst_num, netif); + sched_yield(); + } +} + +/** + * Handler for CPSW Transmit interrupt + * + * @param netif the lwip network interface structure for this ethernetif + * @return none + */ +void +cpswif_tx_inthandler(const u32_t inst_num) { + struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num]; + sem_t* const txsem = &cpswinst->txsem; + const u32_t cpdma_base = cpswinst->cpdma_base; + unsigned int curr_bd; + + sem_post(txsem); + + /* Get the bd which contains the earliest filled data */ + curr_bd = CPSWCPDMATxCPRead(cpdma_base, 0); + /* Acknowledge that this packet is processed */ + CPSWCPDMATxCPWrite(cpdma_base, 0, (unsigned int)curr_bd); + CPSWCPDMAEndOfIntVectorWrite(cpdma_base, CPSW_EOI_TX_PULSE); +} + +static void process_tx_end(const u32_t inst_num) +{ + struct txch *txch; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + volatile struct cpdma_tx_bd *curr_bd, *send_head; + volatile u32_t cnt = 0xFFFF; + + txch = &(cpswinst->txch); + + send_head = txch->send_head; + + curr_bd = send_head; + + /* Check for correct start of packet */ + while((curr_bd->flags_pktlen) & CPDMA_BUF_DESC_SOP) { + + /* Make sure that the transmission is over */ + while(((curr_bd->flags_pktlen & CPDMA_BUF_DESC_OWNER) + == CPDMA_BUF_DESC_OWNER) && ((--cnt) != 0)); + + /* If CPDMA failed to transmit, give it a chance once more */ + if(0 == cnt) { + CPSWCPDMATxHdrDescPtrWrite(cpswinst->cpdma_base, + (u32_t)(curr_bd), 0); + return; + } + + /* One buffer descriptor is free now */ + txch->free_num++; + + /* Traverse till the end of packet is reached */ + while(((curr_bd->flags_pktlen) & CPDMA_BUF_DESC_EOP) != CPDMA_BUF_DESC_EOP) { + curr_bd = curr_bd->next; + + /* As this bd is not the end, its free now */ + txch->free_num++; + + if(txch->free_num == (SIZE_CPPI_RAM >> 1) / sizeof(cpdma_tx_bd)) { + break; + } + } + + send_head->flags_pktlen &= ~(CPDMA_BUF_DESC_SOP); + curr_bd->flags_pktlen &= ~(CPDMA_BUF_DESC_EOP); + + /** + * If there are no more data transmitted, the next interrupt + * shall happen with the pbuf associated with the free_head + */ + if(curr_bd->next == NULL) { + txch->send_head = txch->free_head; + } else { + txch->send_head = curr_bd->next; + } + + pbuf_free((struct pbuf *)curr_bd->pbuf); + + LINK_STATS_INC(link.xmit); + + send_head = txch->send_head; + curr_bd = send_head; + } +} + +static void +tx_thread_function(void* arg) { +struct netif * const netif = arg; +struct cpswportif * const cpswif = netif->state; +const u32_t inst_num = cpswif->inst_num; +struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num]; +sem_t* const txsem = &cpswinst->txsem; +sys_mutex_t* const txmtx = &cpswinst->txmtx; +while (1) { + /* Wait for receive task to wakeup */ + sem_wait(txsem); + sys_mutex_lock(txmtx); + process_tx_end(inst_num); + sys_mutex_unlock(txmtx); + sched_yield(); + } +} + +/** + * Gets the netif status + * + * @param netif The netif whoes status to be checked + * @return The netif status + */ +u32_t +cpswif_netif_status(struct netif *netif) { + return ((u32_t)(netif_is_up(netif))); +} + +/** + * Returns the link status + * + * @param inst_num The instance number of the module + * @param slv_port_num The slave port number for the module + * + * @return the link status + */ +u32_t +cpswif_link_status(u32_t inst_num, u32_t slv_port_num) { + + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + + return (PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[slv_port_num - 1].phy_addr, 3)); +} + +/** + * Checks the value is in the range of min and max + * + * @param vlaue Value + * @param min Minimum Value + * @param max Maximum Value + * + * @return the status + */ +static u32_t +check_valid(u32_t value, u32_t min, u32_t max) { + if ((min <= value) && (value <= max)) + return TRUE; + else + return FALSE; +} + +/* + * Executes following CPSW Configutarions + * Switch Configuration (CPSW_SWITCH_CONFIG has to be defined) + * 1 - Add a multicast entry + * 2 - Add a unicast entry + * 3 - Add a OUI entry + * 4 - Search address in entry list + * 5 - Delete a multicast entry + * 6 - Delete a unicast entry + * 7 - Adds a vlan entry + * 8 - Search vlan in entry list + * 9 - Delete vlan + * 10 - Configure Port Vlan (ID, CFI, PRI) + * 11 - Age Out the Untouched entries of ALE Table + * 12 - Print Dump of Switch + * 13 - Print Dump of Switch Config + * 14 - ALE VLAN Aware Config + * 15 - Configure Rate Limit for TX or RX + * 16 - Enable Engress Check + * 17 - Set port unknown VLAN info + * 18 - Enable MAC Auth + * 19 - Configure Port State + * Phy Configuration + * 1 - Configure PHY of a port + * + * @param cpsw_switch_config parameters required for configuration + * + * @return None +*/ +void +cpsw_switch_configuration(struct cpsw_config *cpsw_config) { + struct cpswinst *cpswinst = &cpsw_inst_data[cpsw_config->cpsw_inst]; + struct cpsw_phy_param *cpsw_phy_param = cpsw_config->phy_param; +#ifdef CPSW_SWITCH_CONFIG + struct cpsw_switch_param *cpsw_switch_param = cpsw_config->switch_param; + s32_t ret; +#endif /* CPSW_SWITCH_CONFIG */ + + switch (cpsw_config->cmd) { +#ifdef CPSW_SWITCH_CONFIG + case CONFIG_SWITCH_ADD_MULTICAST: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_MULTICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_mask, MIN_PORT_MASK, + MAX_PORT_MASK)) { + cpsw_config->ret = ERR_PORT_MASK; + break; + } + + if (!check_valid(cpsw_switch_param->super, MIN_SUPER, MAX_SUPER)) { + cpsw_config->ret = ERR_SUPER; + break; + } + + if (!check_valid(cpsw_switch_param->fwd_state, MIN_MCAST_FWD_STATE, + MAX_MCAST_FWD_STATE)) { + cpsw_config->ret = ERR_MCAST_FWD_STATE; + break; + } + + if (cpsw_switch_param->vid == 0) + ret = cpswif_ale_multicastentry_add(cpswinst, + cpsw_switch_param->port_mask, + cpsw_switch_param->addr, + cpsw_switch_param->super, + cpsw_switch_param->fwd_state); + else + ret = cpswif_ale_vlan_add_mcast(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_mask, + cpsw_switch_param->addr, + cpsw_switch_param->super, + cpsw_switch_param->fwd_state); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_ADD_UNICAST: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_UNICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->ucast_flags, MIN_UCAST_FLAGS, + MAX_UCAST_FLAGS)) { + cpsw_config->ret = ERR_UCAST_FLAGS; + break; + } + + if (!check_valid(cpsw_switch_param->ucast_type, MIN_UCAST_FLAGS, + MAX_UCAST_FLAGS)) { + cpsw_config->ret = ERR_UCAST_FLAGS; + break; + } + + if (cpsw_switch_param->vid == 0) + ret = cpswif_ale_unicastentry_add(cpswinst, cpsw_switch_param->port_num, + cpsw_switch_param->addr, + cpsw_switch_param->ucast_flags, + cpsw_switch_param->ucast_type); + else + ret = cpswif_ale_vlan_add_ucast(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_num, + cpsw_switch_param->addr, + cpsw_switch_param->ucast_flags, + cpsw_switch_param->ucast_type); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_ADD_OUI: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_UNICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + ret = cpswif_ale_OUI_add(cpswinst, cpsw_switch_param->addr); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_FIND_ADDR: + { + if (!check_valid_addr(cpsw_switch_param->addr, 0)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + ret = cpswif_ale_entry_match_addr(cpswinst, cpsw_switch_param->addr, + cpsw_switch_param->vid); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_DEL_MULTICAST: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_MULTICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_mask, MIN_PORT_MASK, + MAX_PORT_MASK)) { + cpsw_config->ret = ERR_PORT_MASK; + break; + } + + if (cpsw_switch_param->vid == 0) + ret = cpswif_ale_multicastentry_del(cpswinst, cpsw_switch_param->port_mask, + cpsw_switch_param->addr); + else + ret = cpswif_ale_vlan_del_mcast(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_mask, + cpsw_switch_param->addr); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_DEL_UNICAST: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_UNICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (cpsw_switch_param->vid == 0) + ret = cpswif_ale_unicastentry_del(cpswinst, cpsw_switch_param->port_num, + cpsw_switch_param->addr); + else + ret = cpswif_ale_vlan_del_ucast(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_num, + cpsw_switch_param->addr); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_ADD_VLAN: + { + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->reg_multi, MIN_VLAN_MCAST_REG, + MAX_VLAN_MCAST_REG)) { + cpsw_config->ret = ERR_VLAN_MCAST_REG; + break; + } + + if (!check_valid(cpsw_switch_param->unreg_multi, MIN_VLAN_MCAST_UNREG, + MAX_VLAN_MCAST_UNREG)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNREG; + break; + } + + if (!check_valid(cpsw_switch_param->untag_port, MIN_VLAN_UNTAG, + MAX_VLAN_UNTAG)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNTAG; + break; + } + + ret = cpswif_ale_vlan_add(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_num, + cpsw_switch_param->untag_port, + cpsw_switch_param->reg_multi, + cpsw_switch_param->unreg_multi); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_FIND_VLAN: + { + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + ret = cpswif_ale_entry_match_vlan(cpswinst, cpsw_switch_param->vid); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_DEL_VLAN: + { + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + ret = cpswif_ale_vlan_del(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_num); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_PORT_VLAN_CONFIG: + { + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->cfi_port, MIN_CFI, MAX_CFI)) { + cpsw_config->ret = ERR_CFI; + break; + } + + if (!check_valid(cpsw_switch_param->prio_port, MIN_PRI, MAX_PRI)) { + cpsw_config->ret = ERR_PRI; + break; + } + + if (cpsw_switch_param->port_num == 0) + CPSWPortVLANConfig(cpswinst->host_port_base, cpsw_switch_param->vid, + cpsw_switch_param->cfi_port, + cpsw_switch_param->prio_port); + else if (cpsw_switch_param->port_num <= 2) + CPSWPortVLANConfig( + cpswinst->port[cpsw_switch_param->port_num - 1].port_base, + cpsw_switch_param->vid, + cpsw_switch_param->cfi_port, + cpsw_switch_param->prio_port); + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_AGEOUT: + { + CPSWALEAgeOut(cpswinst->ale_base); + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_DUMP: + { + u32_t cnt = 0; + + for(cnt = 0; cnt <= MAX_ALE_ENTRIES; cnt++) + { + CPSWALETableEntryGet(cpswinst->ale_base, cnt, + cpsw_config->buf[cnt]); + } + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_CONFIG_DUMP: + { + + if (!check_valid(cpsw_switch_param->ale_tbl_index, MIN_ALE_ENTRY_IDX, + MAX_ALE_ENTRY_IDX)) { + cpsw_config->ret = ERR_ALE_ENTRY_IDX; + break; + } + + CPSWALETableEntryGet(cpswinst->ale_base, cpsw_switch_param->ale_tbl_index, + cpsw_config->ale_entry); + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_VLANAWARE: + { + if (!check_valid(cpsw_switch_param->vlan_aware, MIN_VLANAWARE, + MAX_VLANAWARE)) { + cpsw_config->ret = ERR_VLANAWARE; + break; + } + + if (!check_valid(cpsw_switch_param->drop_packet, MIN_ALE_VLANAWARE, + MAX_ALE_VLANAWARE)) { + cpsw_config->ret = ERR_ALE_VLANAWARE; + break; + } + + if (!cpsw_switch_param->vlan_aware) + CPSWVLANAwareDisable(cpswinst->ale_base); + else + CPSWVLANAwareEnable(cpswinst->ale_base); + + if (!cpsw_switch_param->drop_packet) + CPSWALEVLANAwareClear(cpswinst->ale_base); + else + CPSWALEVLANAwareSet(cpswinst->ale_base); + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_RATELIMIT: + { + if (!check_valid(cpsw_switch_param->enable, MIN_ENABLE, MAX_ENABLE)) { + cpsw_config->ret = ERR_ENABLE; + break; + } + + if (!check_valid(cpsw_switch_param->direction, MIN_DIRECTION, + MAX_DIRECTION)) { + cpsw_config->ret = ERR_DIRECTION; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->addr_type, MIN_ADDR_TYPE, MAX_ADDR_TYPE)) { + cpsw_config->ret = ERR_ADDR_TYPE; + break; + } + + if (!check_valid(cpsw_switch_param->limit, MIN_LIMIT, MAX_LIMIT)) { + cpsw_config->ret = ERR_LIMIT; + break; + } + + cpswif_rate_limit(cpswinst, cpsw_switch_param->enable, + cpsw_switch_param->direction, + cpsw_switch_param->port_num, cpsw_switch_param->addr_type, + cpsw_switch_param->limit); + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_VID_INGRESS_CHECK: + { + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + CPSWALEVIDIngressCheckSet(cpswinst->ale_base, cpsw_switch_param->port_num); + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_ADD_UNKNOWN_VLAN_INFO: + { + if (!check_valid(cpsw_switch_param->unknown_vlan, MIN_UNKNOWN_VLAN, + MAX_UNKNOWN_VLAN)) { + cpsw_config->ret = ERR_UNKNOWN_VLAN; + break; + } + + if (cpsw_switch_param->unknown_vlan == ALE_PORT_UNTAGGED_EGRESS) { + if (!check_valid(cpsw_switch_param->untag_port, MIN_VLAN_UNTAG, + MAX_VLAN_UNTAG)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNTAG; + break; + } + + CPSWALEUnknownUntaggedEgressSet(cpswinst->ale_base, + cpsw_switch_param->untag_port); + } else if (cpsw_switch_param->unknown_vlan == + ALE_PORT_UNKNOWN_REG_MCAST_FLOOD) { + if (!check_valid(cpsw_switch_param->reg_multi, MIN_VLAN_MCAST_REG, + MAX_VLAN_MCAST_REG)) { + cpsw_config->ret = ERR_VLAN_MCAST_REG; + break; + } + + CPSWALEUnknownRegFloodMaskSet(cpswinst->ale_base, + cpsw_switch_param->reg_multi); + } else if (cpsw_switch_param->unknown_vlan == + ALE_PORT_UNKNOWN_UNREG_MCAST_FLOOD) { + if (!check_valid(cpsw_switch_param->unreg_multi, MIN_VLAN_MCAST_UNREG, + MAX_VLAN_MCAST_UNREG)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNREG; + break; + } + + CPSWALEUnknownUnRegFloodMaskSet(cpswinst->ale_base, + cpsw_switch_param->unreg_multi); + } else if (cpsw_switch_param->unknown_vlan == + ALE_PORT_UNKNOWN_VLAN_MEMBER) { + if (!check_valid(cpsw_switch_param->port_mask, MIN_PORT_MASK, + MIN_PORT_MASK)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNREG; + break; + } + + CPSWALEUnknownMemberListSet(cpswinst->ale_base, + cpsw_switch_param->port_mask); + } + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_MACAUTH: + { + if (!check_valid(cpsw_switch_param->mac_auth, MIN_MAC_AUTH, MAX_MAC_AUTH)) { + cpsw_config->ret = ERR_MAC_AUTH; + break; + } + + if (!cpsw_switch_param->mac_auth) + CPSWALEAUTHModeClear(cpswinst->ale_base); + else + CPSWALEAUTHModeSet(cpswinst->ale_base); + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_PORT_STATE: + { + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->port_state, MIN_PORT_STATE, + MAX_PORT_STATE)) { + cpsw_config->ret = ERR_PORT_STATE; + break; + } + + CPSWALEPortStateSet(cpswinst->ale_base, cpsw_switch_param->port_num, + cpsw_switch_param->port_state); + cpsw_config->ret = ERR_PASS; + break; + } +#endif /* CPSW_SWITCH_CONFIG */ + + case CONFIG_SWITCH_SET_PORT_CONFIG: + { + if (!check_valid(cpsw_phy_param->slv_port_num, MIN_SLV_PORT, + MAX_SLV_PORT)) { + cpsw_config->ret = ERR_SLV_PORT; + break; + } + + if (!check_valid(cpsw_phy_param->autoneg, MIN_AUTONEG, MAX_AUTONEG)) { + cpsw_config->ret = ERR_AUTONEG; + break; + } + + if (TRUE == cpsw_phy_param->autoneg) { + if (!check_valid(cpsw_phy_param->config, MIN_PHY_CONFIG, + MAX_PHY_CONFIG)) { + cpsw_config->ret = ERR_PHY_CONFIG; + break; + } + } else { + if (!check_valid(cpsw_phy_param->speed, MIN_SPEED, MAX_SPEED)) { + cpsw_config->ret = ERR_SPEED; + break; + } + + if (!check_valid(cpsw_phy_param->duplex, MIN_DUPLEX, MAX_DUPLEX)) { + cpsw_config->ret = ERR_DUPLEX; + break; + } + } + + if (cpsw_phy_param->autoneg) + cpswif_phy_autoneg(cpswinst, cpsw_phy_param->slv_port_num, + cpsw_phy_param->config); + else + cpswif_phy_forced(cpswinst, cpsw_phy_param->slv_port_num, + cpsw_phy_param->speed, + cpsw_phy_param->duplex); + + cpsw_config->ret = ERR_PASS; + break; + } + + default: + cpsw_config->ret = ERR_INVAL; + break; + } +} |