--- /dev/null +++ b/drivers/net/cns3xxx/cns3xxx_config.h @@ -0,0 +1,136 @@ +/******************************************************************************* + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +#include + +#ifndef CNS3XXX_CONFIG_H +#define CNS3XXX_CONFIG_H + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27) +#define LINUX2627 1 +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) +#define LINUX2631 1 +#endif + + +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +#define CNS3XXX_VLAN_8021Q +#endif + +#ifdef CNS3XXX_VLAN_8021Q +//#define CNS3XXX_NIC_MODE_8021Q // use NIC mode to support 8021Q + +#endif + +#define CONFIG_CNS3XXX_JUMBO_FRAME + +#ifdef CONFIG_CNS3XXX_JUMBO_FRAME +#define MAX_PACKET_LEN 9600 +#else +#define MAX_PACKET_LEN 1536 +#endif + +//#define CONFIG_SWITCH_BIG_ENDIAN + +//#define CONFIG_FPGA_FORCE + +//#define CNS3XXX_GIGA_MODE + +#define CNS3XXX_SET_ARL_TABLE +#define CNS3XXX_AGE_ENABLE +#define CNS3XXX_LEARN_ENABLE +#define CNS3XXX_CPU_PORT_FC +#define CNS3XXX_CPU_MIB_COUNTER +#define CNS3XXX_MAC0_MIB_COUNTER +#define CNS3XXX_MAC1_MIB_COUNTER +//#define CNS3XXX_MAC2_MIB_COUNTER +//#define QOS_TEST +//#define ACCEPT_CRC_BAD_PKT +//#define CONFIG_FAST_BRIDGE +//#define CONFIG_HOLP_TEST + + +#define CONFIG_CNS3XXX_NAPI +#ifdef CONFIG_CNS3XXX_NAPI +#define CNS3XXX_NAPI_WEIGHT 64 +#endif +//#define CONFIG_NIC_MODE +//#define CNS3XXX_TX_HW_CHECKSUM +//#define CNS3XXX_RX_HW_CHECKSUM +//#define CNS3XXX_STATUS_ISR +//#define CNS3XXX_TEST_ONE_LEG_VLAN +//#define CNS3XXX_TX_DSCP_PROC + + +#define CNS3XXX_FSQF_RING0_ISR +//#define CNS3XXX_TSTC_RING0_ISR +//#define CNS3XXX_TSTC_RING1_ISR + +//#define CNS3XXX_COMPARE_PACKET +//#define CONFIG_FPGA_10 +//#define CNS3XXX_CONFIG_SIM_MODE + +#define CNS3XXX_8021Q_HW_TX + + +#ifndef CONFIG_CNS3XXX_SPPE +#define IVL // if no define, use SVL +#endif +//#define CNS3XXX_4N // if don't define it, use 4N+2 + +//#define NCNB_TEST +//#define CNS3XXX_TEST_D_CACHE +#define CNS3XXX_FREE_TX_IN_RX_PATH + + +//#define DEBUG_RX +//#define DEBUG_TX +//#define DEBUG_PRIO_IPDSCR +#define DEBUG_RX_PROC +#define DEBUG_TX_PROC +//#define DEBUG_PHY_PROC +#define CNS3XXX_PVID_PROC +#define CNS3XXX_SARL_PROC + + +//#define DOUBLE_RING_TEST + +//#define CNS3XXX_DOUBLE_RX_RING +//#define CNS3XXX_DOUBLE_TX_RING +#define CNS3XXX_USE_MASK + +#define CNS3XXX_CONFIG_CHANGE_TX_RING + +#ifdef CNS3XXX_DOUBLE_RX_RING +#define CNS3XXX_FSQF_RING1_ISR +#endif + +//#define CNS3XXX_DELAYED_INTERRUPT + +#ifdef CNS3XXX_DELAYED_INTERRUPT +#define MAX_PEND_INT_CNT 0x06 +#define MAX_PEND_TIME 0x20 +#endif + +//#define CNS3XXX_ENABLE_RINT1 +#endif --- /dev/null +++ b/drivers/net/cns3xxx/cns3xxx_ethtool.c @@ -0,0 +1,436 @@ +/******************************************************************************* + * + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +//#include +#include +#include +#include +#include "cns3xxx_symbol.h" +#include "cns3xxx.h" +#include "cns3xxx_tool.h" + +// ethtool support reference e100.c and e1000_ethtool.c . +static void cns3xxx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) +{ + strcpy(info->driver, "cns3xxx"); + strcpy(info->version, DRV_VERSION); + strcpy(info->fw_version, "N/A"); + strcpy(info->bus_info, "N/A"); +} + +static void cns3xxx_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + CNS3XXXPrivate *priv = netdev_priv(netdev); + + ring->rx_max_pending = priv->rx_ring->max_ring_size; + ring->tx_max_pending = priv->tx_ring->max_ring_size; + ring->rx_pending = priv->rx_ring->ring_size; + ring->tx_pending = priv->tx_ring->ring_size; +#if 0 + struct nic *nic = netdev_priv(netdev); + struct param_range *rfds = &nic->params.rfds; + struct param_range *cbs = &nic->params.cbs; + + ring->rx_max_pending = rfds->max; + ring->tx_max_pending = cbs->max; + ring->rx_mini_max_pending = 0; + ring->rx_jumbo_max_pending = 0; + ring->rx_pending = rfds->count; + ring->tx_pending = cbs->count; + ring->rx_mini_pending = 0; + ring->rx_jumbo_pending = 0; +#endif +} + + + +static int cns3xxx_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) +{ + int cns3xxx_up(void); + int cns3xxx_down(void); + int cns3xxx_close(struct net_device *dev); + int cns3xxx_open(struct net_device *dev); + extern struct net_device *net_dev_array[]; + + CNS3XXXPrivate *priv = netdev_priv(netdev); + + int i=0; + +#if 0 + struct nic *nic = netdev_priv(netdev); + struct param_range *rfds = &nic->params.rfds; + struct param_range *cbs = &nic->params.cbs; + + if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) + return -EINVAL; + + if(netif_running(netdev)) + e100_down(nic); + rfds->count = max(ring->rx_pending, rfds->min); + rfds->count = min(rfds->count, rfds->max); + cbs->count = max(ring->tx_pending, cbs->min); + cbs->count = min(cbs->count, cbs->max); + DPRINTK(DRV, INFO, "Ring Param settings: rx: %d, tx %d\n", + rfds->count, cbs->count); + if(netif_running(netdev)) + e100_up(nic); + +#endif + //ring->rx_max_pending = RX_DESC_SIZE; + //ring->tx_max_pending = TX_DESC_SIZE; + +#if 0 + printk("ring->rx_max_pending: %d\n", ring->rx_max_pending); + printk("ring->tx_max_pending: %d\n", ring->tx_max_pending); + printk("ring->rx_pending: %d\n", ring->rx_pending); + printk("ring->tx_pending: %d\n", ring->tx_pending); +#endif + + for (i=0 ; i < NETDEV_SIZE ; ++i) { + if(net_dev_array[i] && netif_running(net_dev_array[i])) { + //printk("close net_dev_array[%d]: %s\n", i, net_dev_array[i]); + cns3xxx_close(net_dev_array[i]); + } + } + + //cns3xxx_down(); + + priv->rx_ring->ring_size = min(ring->rx_pending, priv->rx_ring->max_ring_size); + priv->tx_ring->ring_size = min(ring->rx_pending, priv->tx_ring->max_ring_size); + + for (i=0 ; i < NETDEV_SIZE ; ++i) { + if(net_dev_array[i] && netif_running(net_dev_array[i])) { + //printk("open net_dev_array[%d]: %s\n", i, net_dev_array[i]); + cns3xxx_open(net_dev_array[i]); + } + } + //cns3xxx_up(); + + return 0; +} + +static uint32_t cns3xxx_get_tx_csum(struct net_device *netdev) +{ + //return (netdev->features & NETIF_F_HW_CSUM) != 0; + return (netdev->features & NETIF_F_IP_CSUM) != 0; +} + +static int cns3xxx_set_tx_csum(struct net_device *netdev, uint32_t data) +{ + if (data) + netdev->features |= NETIF_F_IP_CSUM; + else + netdev->features &= ~NETIF_F_IP_CSUM; + return 0; +} + +static uint32_t cns3xxx_get_rx_csum(struct net_device *netdev) +{ + //struct e1000_adapter *adapter = netdev_priv(netdev); + //return adapter->rx_csum; + return 1; +} + +static int cns3xxx_set_rx_csum(struct net_device *netdev, uint32_t data) +{ + return 0; +} + +u32 cns3xxx_get_sg(struct net_device *dev) +{ +#ifdef NETIF_F_SG + return (dev->features & NETIF_F_SG) != 0; +#else + return 0; +#endif +} + +int cns3xxx_set_sg(struct net_device *dev, u32 data) +{ +#ifdef NETIF_F_SG + if (data) + dev->features |= NETIF_F_SG; + else + dev->features &= ~NETIF_F_SG; +#endif + + return 0; +} + +static void cns3xxx_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) +{ + u32 mac_port_config = 0; + CNS3XXXPrivate *priv = netdev_priv(netdev); + + switch (priv->net_device_priv->which_port) + { + case MAC_PORT0: + { + mac_port_config = MAC0_CFG_REG; + break; + } + case MAC_PORT1: + { + mac_port_config = MAC1_CFG_REG; + break; + } + case MAC_PORT2: + { + mac_port_config = MAC2_CFG_REG; + break; + } + } + + + pause->autoneg = ( ((mac_port_config >> 7) & 1) ? AUTONEG_ENABLE : AUTONEG_DISABLE); + pause->tx_pause = (mac_port_config >> 6) & 1; + pause->rx_pause = (mac_port_config >> 5) & 1; +} + +static int cns3xxx_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) +{ + u32 mac_port_config = 0; + CNS3XXXPrivate *priv = netdev_priv(netdev); + + switch (priv->net_device_priv->which_port) + { + case MAC_PORT0: + { + mac_port_config = MAC0_CFG_REG; + break; + } + case MAC_PORT1: + { + mac_port_config = MAC1_CFG_REG; + break; + } + case MAC_PORT2: + { + mac_port_config = MAC2_CFG_REG; + break; + } + } + + + mac_port_config &= ~(0x1 << 7); // clean AN + mac_port_config &= ~(0x1 << 11); // clean rx flow control + mac_port_config &= ~(0x1 << 12); // clean tx flow control + + mac_port_config |= ( (pause->autoneg << 7) | (pause->rx_pause << 11) | (pause->tx_pause << 12) ); + + + switch (priv->net_device_priv->which_port) + { + case MAC_PORT0: + { + MAC0_CFG_REG = mac_port_config; + break; + } + case MAC_PORT1: + { + MAC1_CFG_REG = mac_port_config; + break; + } + case MAC_PORT2: + { + MAC2_CFG_REG = mac_port_config; + break; + } + } + return 0; +} + +u32 cns3xxx_get_link(struct net_device *netdev) +{ + u32 mac_port_config = 0; + CNS3XXXPrivate *priv = netdev_priv(netdev); + + switch (priv->net_device_priv->which_port) + { + case MAC_PORT0: + { + mac_port_config = MAC0_CFG_REG; + break; + } + case MAC_PORT1: + { + mac_port_config = MAC1_CFG_REG; + break; + } + case MAC_PORT2: + { + mac_port_config = MAC2_CFG_REG; + break; + } + } + + return (mac_port_config & 1 ) ? 1 : 0; + //return netif_carrier_ok(dev) ? 1 : 0; +} + + +static int cns3xxx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +{ + u8 value; + u32 mac_port_config = 0; + CNS3XXXPrivate *priv = netdev_priv(netdev); + + + if (priv->net_device_priv->nic_setting == 0) { // connect to switch chip + + GET_MAC_PORT_CFG(priv->net_device_priv->which_port, mac_port_config) + + ecmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full| SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_Pause); + + ecmd->duplex = ((mac_port_config >> 4) & 0x1) ? DUPLEX_FULL : DUPLEX_HALF ; + + value = ((mac_port_config >> 2) & 0x3); + switch (value) + { + case 0: + ecmd->speed = SPEED_10; + break; + case 1: + ecmd->speed = SPEED_100; + break; + case 2: + ecmd->speed = SPEED_1000; + break; + } + + ecmd->autoneg = ((mac_port_config >> 7) & 1) ? AUTONEG_ENABLE : AUTONEG_DISABLE; + + + + } else { // connect to PHY chip + + } + + return 0; +} + +// set speed and duplex +int cns3xxx_set_spd_dplx(struct net_device *netdev, u16 spddplx) +{ + u32 mac_port_config = 0; + CNS3XXXPrivate *priv = netdev_priv(netdev); + + GET_MAC_PORT_CFG(priv->net_device_priv->which_port, mac_port_config) + + //printk("mac_port_config: %x\n", mac_port_config); + + mac_port_config &= ~(0x3 << 8); // clear speed + mac_port_config &= ~(0x1 << 10); // clear duplex + mac_port_config &= ~(0x1 << 7); // disable AN + + switch (spddplx) { + case AUTONEG_ENABLE: + mac_port_config |= (0x1 << 7); // enable AN + break; + case SPEED_10 + DUPLEX_HALF: + printk("10, halt\n"); + mac_port_config |= (0 << 8); // set speed + mac_port_config |= (0 << 10); // set duplex + //printk("xxx mac_port_config: %x\n", mac_port_config); + break; + case SPEED_10 + DUPLEX_FULL: + mac_port_config |= (0 << 8); // set speed + mac_port_config |= (1 << 10); // set duplex + break; + case SPEED_100 + DUPLEX_HALF: + mac_port_config |= (1 << 8); // set speed + mac_port_config |= (0 << 10); // set duplex + break; + case SPEED_100 + DUPLEX_FULL: + mac_port_config |= (1 << 8); // set speed + mac_port_config |= (1 << 10); // set duplex + break; + case SPEED_1000 + DUPLEX_HALF: + mac_port_config |= (2 << 8); // set speed + mac_port_config |= (0 << 10); // set duplex + break; + case SPEED_1000 + DUPLEX_FULL: + mac_port_config |= (2 << 8); // set speed + mac_port_config |= (1 << 10); // set duplex + break; + default: + //printk("Unsupported Speed/Duplex configuration\n"); + return -EINVAL; + } + + SET_MAC_PORT_CFG(priv->net_device_priv->which_port, mac_port_config) + + return 0; +} + +static int cns3xxx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +{ + u8 value = 0; + CNS3XXXPrivate *priv = netdev_priv(netdev); + + if (priv->net_device_priv->nic_setting == 0) { // connect to switch chip + if (ecmd->autoneg == AUTONEG_ENABLE) { + printk("autoneg\n"); + if ((value=cns3xxx_set_spd_dplx(netdev, AUTONEG_ENABLE)) != 0) { + return -EINVAL; + } + } else { + printk("no autoneg\n"); + if ((value=cns3xxx_set_spd_dplx(netdev, ecmd->speed + ecmd->duplex)) != 0) { + return -EINVAL; + } + + + } + + } else { // connect to PHY chip + + } + + // down then up + return 0; +} + +static const struct ethtool_ops cns3xxx_ethtool_ops = { + .get_drvinfo = cns3xxx_get_drvinfo, + .get_ringparam = cns3xxx_get_ringparam, + .set_ringparam = cns3xxx_set_ringparam, + .get_rx_csum = cns3xxx_get_rx_csum, + .set_rx_csum = cns3xxx_set_rx_csum, + .get_tx_csum = cns3xxx_get_tx_csum, + .set_tx_csum = cns3xxx_set_tx_csum, + .get_sg = cns3xxx_get_sg, + .set_sg = cns3xxx_set_sg, + .get_pauseparam = cns3xxx_get_pauseparam, + .set_pauseparam = cns3xxx_set_pauseparam, + .get_link = cns3xxx_get_link, + .get_settings = cns3xxx_get_settings, + .set_settings = cns3xxx_set_settings, +}; + +void cns3xxx_set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &cns3xxx_ethtool_ops); +} --- /dev/null +++ b/drivers/net/cns3xxx/cns3xxx.h @@ -0,0 +1,452 @@ +/******************************************************************************* + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +#ifndef CNS3XXX_H +#define CNS3XXX_H + +#include "cns3xxx_symbol.h" +#include "cns3xxx_config.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +#include +#endif + +//#define VERSION "1.0" + + +typedef struct +{ + int32_t sdp; // segment data pointer + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + u32 cown:1; + u32 eor:1; + u32 fsd:1; + u32 lsd:1; + u32 interrupt:1; + u32 fr:1; + u32 fp:1; // force priority + u32 pri:3; + u32 rsv_1:3; // reserve + u32 ico:1; + u32 uco:1; + u32 tco:1; + u32 sdl:16; // segment data length + +#else + u32 sdl:16; // segment data length + u32 tco:1; + u32 uco:1; + u32 ico:1; + u32 rsv_1:3; // reserve + u32 pri:3; + u32 fp:1; // force priority + u32 fr:1; + u32 interrupt:1; + u32 lsd:1; + u32 fsd:1; + u32 eor:1; + u32 cown:1; +#endif + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + u32 rsv_3:5; + u32 fewan:1; + u32 ewan:1; + u32 mark:3; + u32 pmap:5; + u32 rsv_2:9; + u32 dels:1; + u32 inss:1; + u32 sid:4; + u32 stv:1; + u32 ctv:1; +#else + u32 ctv:1; + u32 stv:1; + u32 sid:4; + u32 inss:1; + u32 dels:1; + u32 rsv_2:9; + u32 pmap:5; + u32 mark:3; + u32 ewan:1; + u32 fewan:1; + u32 rsv_3:5; +#endif + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + u32 s_pri:3; + u32 s_dei:1; + u32 s_vid:12; + u32 c_pri:3; + u32 c_cfs:1; + u32 c_vid:12; +#else + u32 c_vid:12; + u32 c_cfs:1; + u32 c_pri:3; + u32 s_vid:12; + u32 s_dei:1; + u32 s_pri:3; +#endif + + u8 alignment[16]; // for alignment 32 byte + +} __attribute__((packed)) TXDesc; + +typedef struct +{ + u32 sdp; + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + u32 cown:1; + u32 eor:1; + u32 fsd:1; + u32 lsd:1; + u32 hr :6; + u32 prot:4; + u32 ipf:1; + u32 l4f:1; + u32 sdl:16; +#else + u32 sdl:16; + u32 l4f:1; + u32 ipf:1; + u32 prot:4; + u32 hr :6; + u32 lsd:1; + u32 fsd:1; + u32 eor:1; + u32 cown:1; +#endif + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + u32 rsv_3:11; + u32 ip_offset:5; + u32 rsv_2:1; + u32 tc:2; + u32 un_eth:1; + u32 crc_err:1; + u32 sp:3; + u32 rsv_1:2; + u32 e_wan:1; + u32 exdv:1; + u32 iwan:1; + u32 unv:1; + u32 stv:1; + u32 ctv:1; +#else + u32 ctv:1; + u32 stv:1; + u32 unv:1; + u32 iwan:1; + u32 exdv:1; + u32 e_wan:1; + u32 rsv_1:2; + u32 sp:3; + u32 crc_err:1; + u32 un_eth:1; + u32 tc:2; + u32 rsv_2:1; + u32 ip_offset:5; + u32 rsv_3:11; +#endif + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + u32 s_pri:3; + u32 s_dei:1; + u32 s_vid:12; + u32 c_pri:3; + u32 c_cfs:1; + u32 c_vid:12; +#else + u32 c_vid:12; + u32 c_cfs:1; + u32 c_pri:3; + u32 s_vid:12; + u32 s_dei:1; + u32 s_pri:3; +#endif + + u8 alignment[16]; // for alignment 32 byte + +} __attribute__((packed)) RXDesc; + +typedef struct { + TXDesc *tx_desc; + struct sk_buff *skb; // for free skb + u32 pri; + unsigned long j; + unsigned long tx_index; +}TXBuffer; + +typedef struct { + RXDesc *rx_desc; + struct sk_buff *skb; // rx path need to fill some skb field, ex: length ... +#ifdef NCNB_TEST + u32 ncnb_index; +#endif +}RXBuffer; + + +typedef struct { + TXBuffer *head; + TXDesc *tx_desc_head_vir_addr; + dma_addr_t tx_desc_head_phy_addr; + u32 cur_index; // for put send packet + spinlock_t tx_lock; + u32 non_free_tx_skb; + u32 free_tx_skb_index; + u32 ring_size; + u32 max_ring_size; +}TXRing; + + +typedef struct { + RXBuffer *head; + RXDesc *rx_desc_head_vir_addr; + dma_addr_t rx_desc_head_phy_addr; + u32 cur_index; + u32 ring_size; + u32 max_ring_size; +}RXRing; + +#if 0 +typedef struct +{ + CNS3XXXIoctlCmd cmd; + TXRing *tx_ring; + RXRing *rx_ring; +}CNS3XXXRingStatus; +#endif + + +#define RX_RING0(priv) (priv->rx_ring[0]) +#define TX_RING0(priv) (priv->tx_ring[0]) + + +static inline u32 get_rx_ring_size(const RXRing *ring) +{ + //printk("rx ring->ring_size: %d\n", ring->ring_size); + return ring->ring_size; +} + +static inline u32 get_tx_ring_size(TXRing *ring) +{ + //printk("tx ring->ring_size: %d\n", ring->ring_size); + return ring->ring_size; +} + +static inline RXBuffer *get_rx_ring_head(const RXRing *rx_ring) +{ + return rx_ring->head; +} + +static inline TXBuffer *get_tx_ring_head(TXRing *tx_ring) +{ + return tx_ring->head; +} + +static inline RXBuffer *get_cur_rx_buffer(RXRing *rx_ring) +{ + return rx_ring->head + rx_ring->cur_index; +} + +static inline TXBuffer *get_cur_tx_buffer(TXRing *tx_ring) +{ + return tx_ring->head + tx_ring->cur_index; +} + +static inline u32 get_rx_head_phy_addr(RXRing *rx_ring) +{ + return rx_ring->rx_desc_head_phy_addr; +} + +static inline u32 get_tx_ring_head_phy_addr(TXRing *tx_ring) +{ + return tx_ring->tx_desc_head_phy_addr; +} + + +static inline u32 get_rx_cur_index(RXRing *rx_ring) +{ + return rx_ring->cur_index; +} + +static inline u32 get_tx_cur_index(TXRing *tx_ring) +{ + return tx_ring->cur_index; +} + +static inline u32 get_tx_cur_phy_addr(u8 ring_num) +{ + if (ring_num == 0) + return TS_DESC_PTR0_REG; + if (ring_num == 1) + return TS_DESC_PTR1_REG; + return 0; // fail +} + +static inline void rx_index_next(RXRing *ring) +{ + ring->cur_index = ((ring->cur_index + 1) % ring->ring_size); +} +static inline void tx_index_next(TXRing *ring) +{ + ring->cur_index = ((ring->cur_index + 1) % ring->ring_size); +} + + + +struct CNS3XXXPrivate_; + +typedef int (*RXFuncPtr)(struct sk_buff *skb, RXDesc*tx_desc_ptr, const struct CNS3XXXPrivate_* ); +typedef int (*TXFuncPtr)(TXDesc*tx_desc_ptr, const struct CNS3XXXPrivate_*, struct sk_buff *); +typedef void (*OpenPtr)(void); +typedef void (*ClosePtr)(void); + + +// for ethtool set operate +typedef struct{ + +}NICSetting; + +typedef struct{ + int pmap; // for port base, force route + int is_wan; // mean the net device is WAN side. + //u16 gid; + u16 s_tag; + //u8 mac_type; // VLAN base, or port base; + u16 vlan_tag; + + // do port base mode and vlan base mode work + RXFuncPtr rx_func; + TXFuncPtr tx_func; + OpenPtr open; + ClosePtr close; + u8 which_port; + //NICSetting *nic_setting; + u8 *mac; // point to a mac address array + VLANTableEntry *vlan_table_entry; + ARLTableEntry *arl_table_entry; + NICSetting *nic_setting; + const char *name; // 16 bytes, reference include/linux/netdevice.h IFNAMSIZ +}NetDevicePriv; + +typedef struct +{ + u8 num_rx_queues; + u8 num_tx_queues; + TXRing *tx_ring; + RXRing *rx_ring; +}RingInfo; + + +/* store this information for the driver.. */ +typedef struct CNS3XXXPrivate_ +{ + u8 num_rx_queues; + u8 num_tx_queues; + TXRing *tx_ring; + RXRing *rx_ring; + struct net_device_stats stats; + spinlock_t lock; + int pmap; + int is_wan; // mean the net device is WAN side. + u16 gid; + u8 mac_type; // VLAN base, or port base; + u16 vlan_tag; + struct napi_struct napi; + struct work_struct reset_task; + + u8 which_port; + //NICSetting *nic_setting; + char name[IFNAMSIZ]; // 16 bytes, reference include/linux/netdevice.h IFNAMSIZ + + + NetDevicePriv *net_device_priv; + u8 ring_index; + + u32 rx_s_vid[4096]; // record receive s vid (0x9100 ...) + u32 rx_c_vid[4096]; // record receive c vid (0x8100 ...) +#ifdef CONFIG_CNS3XXX_NAPI + volatile unsigned long is_qf; // determine rx ring queue full state +#endif + +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) + struct vlan_group *vlgrp; +#endif +}CNS3XXXPrivate; + + + + +int rx_port_base(struct sk_buff *skb, RXDesc *rx_desc_ptr, const struct CNS3XXXPrivate_ *priv); + +int rx_vlan_base(struct sk_buff *skb, RXDesc *rx_desc_ptr, const struct CNS3XXXPrivate_ *priv); + +int tx_port_base(TXDesc *tx_desc_ptr, const struct CNS3XXXPrivate_ *priv, struct sk_buff *skb); + + +int tx_vlan_base(TXDesc *tx_desc_ptr, const struct CNS3XXXPrivate_ *priv, struct sk_buff *skb); +#if defined (CONFIG_CNS3XXX_SPPE) +int fp_port_base(TXDesc *tx_desc_ptr, const struct CNS3XXXPrivate_ *priv, struct sk_buff *skb); +#endif +#endif + --- /dev/null +++ b/drivers/net/cns3xxx/cns3xxx_main.c @@ -0,0 +1,3949 @@ +/******************************************************************************* + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +#include +#include +#include +#include "cns3xxx.h" +#include "cns3xxx_tool.h" +#include "cns3xxx_config.h" + +#if defined (CONFIG_CNS3XXX_SPPE) +#include +#define PACKET_REASON_TO_CPU (0x2C) +#endif + +#define RX_SDP_ALIGN 64 + +#ifdef CONFIG_FPGA +#include "fpga.h" +#endif + +#ifdef CONFIG_VB +#include "vb.h" +#endif + +#define CPU_CACHE_BYTES 64 +#define CPU_CACHE_ALIGN(X) (((X) + (CPU_CACHE_BYTES-1)) & ~(CPU_CACHE_BYTES-1)) + + +#define QUEUE_WEIGHT_SET(port, ctl) \ +{ \ + MAC##port##_PRI_CTRL_REG &= ~(0x3ffff); \ + MAC##port##_PRI_CTRL_REG |= (ctl.sch_mode << 16); \ + MAC##port##_PRI_CTRL_REG |= (ctl.q0_w); \ + MAC##port##_PRI_CTRL_REG |= (ctl.q1_w << 4); \ + MAC##port##_PRI_CTRL_REG |= (ctl.q2_w << 8); \ + MAC##port##_PRI_CTRL_REG |= (ctl.q3_w << 12); \ +} + +#define QUEUE_WEIGHT_GET(port, ctl) \ +{ \ + ctl.sch_mode = ((MAC##port##_PRI_CTRL_REG >> 16 ) & 0x3); \ + ctl.q0_w = ((MAC##port##_PRI_CTRL_REG >> 0 ) & 0x7); \ + ctl.q1_w = ((MAC##port##_PRI_CTRL_REG >> 4 ) & 0x7); \ + ctl.q2_w = ((MAC##port##_PRI_CTRL_REG >> 8 ) & 0x7); \ + ctl.q3_w = ((MAC##port##_PRI_CTRL_REG >> 12 ) & 0x7); \ +} + +int cns3xxx_send_packet(struct sk_buff *skb, struct net_device *netdev); +static int install_isr_rc = 0; +static int rc_setup_rx_tx = 0; // rc means reference counting. +static struct net_device *intr_netdev; +struct net_device *net_dev_array[NETDEV_SIZE]; +spinlock_t tx_lock; +spinlock_t rx_lock; +u8 fast_bridge_en=1; +u8 show_rx_proc=0; +u8 show_tx_proc=0; + +int init_port=7; // bit map 7 means port 0, 1 and 2, default is 7. +//module_param(init_port, u8, S_IRUGO); +module_param(init_port, int, 0); + +u8 ring_index=0; // 0 or 1 + +#ifdef CNS3XXX_DELAYED_INTERRUPT +static u32 max_pend_int_cnt=MAX_PEND_INT_CNT, max_pend_time=MAX_PEND_TIME; +#endif + +#ifdef CONFIG_CNS3XXX_NAPI +struct net_device *napi_dev; + #ifdef CNS3XXX_DOUBLE_RX_RING + struct net_device *r1_napi_dev; // ring1 napi dev + #endif +#endif + +const u32 MAX_RX_DESC_SIZE = 512; +const u32 MAX_TX_DESC_SIZE = 512; +const u32 RX_DESC_SIZE = 128; +//const u32 RX_DESC_SIZE = 5; +const u32 TX_DESC_SIZE = 120; + +//RXRing *rx_ring; +//TXRing *tx_ring; + +// only for debug (proc) +RingInfo g_ring_info; + +int MSG_LEVEL = NORMAL_MSG; + +#ifdef CNS3XXX_STATUS_ISR +const char *cns3xxx_gsw_status_tbl[] = { + "\nMAC0_Q_FULL\n", + "\nMAC1_Q_FULL\n", + "\nCPU_Q_FULL\n", + "\nHNAT_Q_FULL\n", + "\nMAC2_Q_FULL\n", + "\nMAC0_Q_EXT_FULL\n", + "\nGLOBAL_Q_FULL\n", + "\nBUFFER_FULL\n", + "\nMIB_COUNTER_TH\n", + "\n", // 9 + "\nMAC0_INTRUDER\n", + "\nMAC1_INTRUDER\n", + "\nCPU_INTRUDER\n", + "\nMAC2_INTRUDER\n", + "\nMAC0_STATUS_CHG\n", + "\nMAC1_STATUS_CHG\n", + "\nMAC2_STATUS_CHG\n", + "\nMAC0_NO_LINK_DROP\n", + "\nMAC1_NO_LINK_DROP\n", + "\nMAC2_NO_LINK_DROP\n", + "\nMAC0_RX_ERROR_DROP\n", + "\nMAC1_RX_ERROR_DROP\n", + "\nMAC2_RX_ERROR_DROP\n", + "\nMAC0_NO_DESTINATION_DROP\n", + "\nMAC1_NO_DESTINATION_DROP\n", + "\nMAC2_NO_DESTINATION_DROP\n", + "\nMAC0_RMC_PAUSE_DROP\n", + "\nMAC1_RMC_PAUSE_DROP\n", + "\nMAC2_RMC_PAUSE_DROP\n", + "\nMAC0_LOCAL_DROP\n", + "\nMAC1_LOCAL_DROP\n", + "\nMAC2_LOCAL_DROP\n", +}; +#endif + +#define MIN_PACKET_LEN 14 + +void cns3xxx_write_pri_mask(u8 pri_mask); + +static int cns3xxx_notify_reboot(struct notifier_block *nb, unsigned long event, void *ptr); + +static struct notifier_block cns3xxx_notifier_reboot = { + .notifier_call = cns3xxx_notify_reboot, + .next = NULL, + .priority = 0 +}; + +#if defined(CNS3XXX_VLAN_8021Q) +void cns3xxx_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid); +void cns3xxx_vlan_rx_register(struct net_device *dev, struct vlan_group *grp); +#endif + +void take_off_vlan_header(struct sk_buff *skb) +{ + // take off VLAN header + memmove(skb->data + 4, skb->data, 12); +#if 0 + //skb_ptr->data += 4; + skb_reserve(skb, 4); +#else + skb->data += 4; +#endif + skb->len -= 4; // minus 4 byte vlan tag +} + +int rx_port_base(struct sk_buff *skb, RXDesc *rx_desc_ptr, const struct CNS3XXXPrivate_ *priv) +{ + if (skb->data[12] == 0x81 && skb->data[13] == 0x00) // VLAN header + { + take_off_vlan_header(skb); + print_packet(skb->data, skb->len); + } + return 0; +} + +int rx_vlan_base(struct sk_buff *skb, RXDesc *rx_desc_ptr, const struct CNS3XXXPrivate_ *priv) +{ + return 0; +} + +int tx_port_base(TXDesc *tx_desc_ptr, const struct CNS3XXXPrivate_ *priv, struct sk_buff *skb) +{ +#if defined(CNS3XXX_VLAN_8021Q) && defined (CNS3XXX_8021Q_HW_TX) + if (skb && priv->vlgrp != NULL && vlan_tx_tag_present(skb)) + { + tx_desc_ptr->c_vid = cpu_to_le16(vlan_tx_tag_get(skb)); + tx_desc_ptr->ctv=1; + tx_desc_ptr->fr = 0; + + } + else +#endif + { + tx_desc_ptr->ctv = 0; + tx_desc_ptr->pmap = priv->net_device_priv->pmap; + tx_desc_ptr->fr = 1; + } + + return 0; +} + + +int tx_vlan_base(TXDesc *tx_desc_ptr, const struct CNS3XXXPrivate_ *priv, struct sk_buff *skb) +{ +#if defined(CNS3XXX_VLAN_8021Q) + + if (skb && priv->vlgrp != NULL && vlan_tx_tag_present(skb)) { + tx_desc_ptr->c_vid = cpu_to_le16(vlan_tx_tag_get(skb)); + } +#else + tx_desc_ptr->c_vid = priv->net_device_priv->vlan_tag; + +#endif + tx_desc_ptr->ctv=1; + tx_desc_ptr->fr = 0; + + return 0; +} + +#if defined (CONFIG_CNS3XXX_SPPE) +int fp_port_base(TXDesc *tx_desc_ptr, const struct CNS3XXXPrivate_ *priv, struct sk_buff *skb) +{ +#if 1 + tx_desc_ptr->fr = 1; + tx_desc_ptr->pmap = 0x8; +#else + tx_desc_ptr->fr = 0; + tx_desc_ptr->ctv = 1; + tx_desc_ptr->c_vid = 80; +#endif + return 0; +} +#endif + +static inline struct sk_buff *cns3xxx_alloc_skb(void) +{ + struct sk_buff *skb; + u32 align_64; + + skb = dev_alloc_skb(MAX_PACKET_LEN + 2 + RX_SDP_ALIGN); + + if (unlikely(!skb)) { + return NULL; + } + pci_dma_sync_single_for_device(NULL, virt_to_phys(skb->data), MAX_PACKET_LEN+2+RX_SDP_ALIGN, PCI_DMA_FROMDEVICE); + + align_64=CPU_CACHE_ALIGN((u32)skb->data); + skb_reserve(skb, align_64-(u32)skb->data); /* 16 bytes alignment */ + +#ifndef CNS3XXX_4N + skb_reserve(skb, NET_IP_ALIGN); /* 16 bytes alignment */ +#endif + + + + return skb; +} + +static int free_rx_skb(RXRing *rx_ring) +{ + int i=0; + RXBuffer *rx_buffer = rx_ring->head; + //RXDesc *rx_desc = rx_ring.rx_desc_head_vir_addr; + + for (i=0 ; i < get_rx_ring_size(rx_ring) ; ++i) { + if (rx_buffer->rx_desc->cown==0 && rx_buffer->skb) { + dev_kfree_skb(rx_buffer->skb); + rx_buffer->skb=0; + } + } + return 0; +} + +int cns3xxx_setup_all_rx_resources(RXRing *rx_ring, u8 ring_num) +{ + int i=0; + RXBuffer *rx_buffer = 0; + RXDesc *rx_desc = 0; + +#ifdef NCNB_TEST + ncnb_buf = dma_alloc_coherent(NULL, 2*1024* get_rx_ring_size(rx_ring), &ncnb_buf_phy, GFP_KERNEL); + printk("NCB_BUF: %08X PHY: %08X \n", ncnb_buf, ncnb_buf_phy); + +#endif + + // alloc RXDesc array + rx_ring->rx_desc_head_vir_addr = dma_alloc_coherent(NULL, sizeof(RXDesc) * (get_rx_ring_size(rx_ring)), &rx_ring->rx_desc_head_phy_addr, GFP_KERNEL); + if (!rx_ring->rx_desc_head_vir_addr) { + return -ENOMEM; + } + + memset(rx_ring->rx_desc_head_vir_addr, 0, sizeof(RXDesc) * get_rx_ring_size(rx_ring)); + + // alloc RXBuffer array + rx_ring->head = kmalloc(sizeof(RXBuffer) * get_rx_ring_size(rx_ring), GFP_KERNEL); + + if (!rx_ring->head) { + return -ENOMEM; + } + + rx_buffer = rx_ring->head; + for (i=0 ; i < get_rx_ring_size(rx_ring) ; ++i) { + rx_buffer->skb=0; + ++rx_buffer; + } + + rx_buffer = rx_ring->head; + rx_desc = rx_ring->rx_desc_head_vir_addr; + for (i=0 ; i < get_rx_ring_size(rx_ring) ; ++i, ++rx_buffer, ++rx_desc) { + rx_buffer->rx_desc = rx_desc; + rx_buffer->skb = cns3xxx_alloc_skb(); + + if (!rx_buffer->skb) { + + free_rx_skb(rx_ring); + kfree(rx_ring->head); + dma_free_coherent(NULL, sizeof(RXDesc) * get_rx_ring_size(rx_ring), rx_ring->rx_desc_head_vir_addr, rx_ring->rx_desc_head_phy_addr); + return -ENOMEM; + } + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + { + RXDesc tmp_rx_desc; + + memset(&tmp_rx_desc, 0, sizeof(RXDesc)); + tmp_rx_desc.sdp = (u32)virt_to_phys(rx_buffer->skb->data); + tmp_rx_desc.sdl = MAX_PACKET_LEN; + if (i == (get_rx_ring_size(rx_ring)-1) ){ + tmp_rx_desc.eor = 1; + } + tmp_rx_desc.fsd = 1; + tmp_rx_desc.lsd = 1; + swap_rx_desc(&tmp_rx_desc, rx_buffer->rx_desc); + } + +#else + rx_buffer->rx_desc->sdp = (u32)virt_to_phys(rx_buffer->skb->data); + rx_buffer->rx_desc->sdl = MAX_PACKET_LEN; + if (i == (get_rx_ring_size(rx_ring)-1) ){ + rx_buffer->rx_desc->eor = 1; + } + rx_buffer->rx_desc->fsd = 1; + rx_buffer->rx_desc->lsd = 1; +#endif + + } + rx_ring->cur_index = 0 ; + + if (ring_num == 0){ + FS_DESC_PTR0_REG = rx_ring->rx_desc_head_phy_addr; + FS_DESC_BASE_ADDR0_REG = rx_ring->rx_desc_head_phy_addr; + + } else if (ring_num == 1){ + FS_DESC_PTR1_REG = rx_ring->rx_desc_head_phy_addr; + FS_DESC_BASE_ADDR1_REG = rx_ring->rx_desc_head_phy_addr; + } + + return CAVM_OK; +} + +static int cns3xxx_setup_all_tx_resources(TXRing *tx_ring, u8 ring_num) +{ + int i=0; + TXBuffer *tx_buffer = 0; + TXDesc *tx_desc = 0; + + + spin_lock_init(&(tx_ring->tx_lock)); + + tx_ring->tx_desc_head_vir_addr = dma_alloc_coherent(NULL, sizeof(TXDesc) * get_tx_ring_size(tx_ring), &tx_ring->tx_desc_head_phy_addr, GFP_KERNEL); + if (!tx_ring->tx_desc_head_vir_addr) { + return -ENOMEM; + } + + memset(tx_ring->tx_desc_head_vir_addr, 0, sizeof(TXDesc) * get_tx_ring_size(tx_ring)); + tx_ring->head = kmalloc(sizeof(TXBuffer) * get_tx_ring_size(tx_ring), GFP_KERNEL); + + tx_buffer = tx_ring->head; + tx_desc = tx_ring->tx_desc_head_vir_addr; + for (i=0 ; i < get_tx_ring_size(tx_ring) ; ++i, ++tx_buffer, ++tx_desc) { + tx_buffer->tx_desc = tx_desc; + + tx_buffer->tx_desc->cown = 1; + tx_buffer->skb = 0; + if (i == (get_tx_ring_size(tx_ring)-1) ){ + tx_buffer->tx_desc->eor = 1; + } +#ifdef CONFIG_SWITCH_BIG_ENDIAN + swap_tx_desc(tx_buffer->tx_desc, tx_buffer->tx_desc); +#endif + + } + + tx_ring->cur_index = 0 ; + + if (ring_num == 0){ + TS_DESC_PTR0_REG = tx_ring->tx_desc_head_phy_addr; + TS_DESC_BASE_ADDR0_REG = tx_ring->tx_desc_head_phy_addr; + } else if (ring_num == 1){ + TS_DESC_PTR1_REG = tx_ring->tx_desc_head_phy_addr; + TS_DESC_BASE_ADDR1_REG = tx_ring->tx_desc_head_phy_addr; + } + return CAVM_OK; +} + +int cns3xxx_free_all_rx_resources(RXRing *rx_ring) +{ + free_rx_skb(rx_ring); + kfree(rx_ring->head); + dma_free_coherent(NULL, sizeof(RXDesc) * get_rx_ring_size(rx_ring), rx_ring->rx_desc_head_vir_addr, rx_ring->rx_desc_head_phy_addr); + return 0; +} + +static int free_tx_skb(TXRing *tx_ring) +{ + int i=0; + TXBuffer *tx_buffer = tx_ring->head; + + for (i=0 ; i < get_tx_ring_size(tx_ring) ; ++i) { + if (tx_buffer->skb) { + dev_kfree_skb(tx_buffer->skb); + tx_buffer->skb = 0; + } + } + return 0; +} + +int cns3xxx_free_all_tx_resources(TXRing *tx_ring) +{ + free_tx_skb(tx_ring); + kfree(tx_ring->head); + dma_free_coherent(NULL, sizeof(TXDesc) * get_tx_ring_size(tx_ring), tx_ring->tx_desc_head_vir_addr, tx_ring->tx_desc_head_phy_addr); + return 0; +} + +static int cns3xxx_free_rx_tx_res(CNS3XXXPrivate *priv) +{ + int i=0; + + --rc_setup_rx_tx; + if (rc_setup_rx_tx == 0) { + enable_port(3, 0); // disable cpu port + + // stop RX/TX ring0 dma + enable_rx_dma(0, 0); + enable_tx_dma(0, 0); + + for (i=0 ; i < priv->num_rx_queues ; ++i) { + cns3xxx_free_all_rx_resources(priv->rx_ring+i); + memset(priv->rx_ring + i, 0, sizeof(RXRing)); + } + + for (i=0 ; i < priv->num_tx_queues ; ++i) { + cns3xxx_free_all_tx_resources(priv->tx_ring+i); + memset(priv->tx_ring + i, 0, sizeof(TXRing)); + } + + } + return 0; +} + + +static int cns3xxx_setup_rx_tx_res(CNS3XXXPrivate *priv) +{ + int i=0; + + if (rc_setup_rx_tx == 0) { + clear_fs_dma_state(1); + FS_DESC_PTR0_REG = 0; + FS_DESC_BASE_ADDR0_REG = 0; + FS_DESC_PTR1_REG = 0; + FS_DESC_BASE_ADDR1_REG = 0; + TS_DESC_PTR0_REG = 0; + TS_DESC_BASE_ADDR0_REG = 0; + TS_DESC_PTR1_REG = 0; + TS_DESC_BASE_ADDR1_REG = 0; + + for (i=0 ; i < priv->num_tx_queues ; ++i) { + spin_lock_init(&((priv->tx_ring+i)->tx_lock)); + (priv->tx_ring+i)->max_ring_size = MAX_TX_DESC_SIZE; + (priv->tx_ring+i)->ring_size = TX_DESC_SIZE; + if (cns3xxx_setup_all_tx_resources(priv->tx_ring+i, i) != CAVM_OK) + return CAVM_ERR; + } + + for (i=0 ; i < priv->num_rx_queues ; ++i) { + (priv->rx_ring+i)->max_ring_size = MAX_RX_DESC_SIZE; + (priv->rx_ring+i)->ring_size = RX_DESC_SIZE; + if (cns3xxx_setup_all_rx_resources(priv->rx_ring+i, i) != CAVM_OK) + return CAVM_ERR; + + } + clear_fs_dma_state(0); + } + ++rc_setup_rx_tx; + return CAVM_OK; +} + +int free_tx_desc_skb(TXRing *tx_ring, u8 ring_num) +{ +#if 1 + int i=0; + //u32 tssd_current=0; + TXBuffer *tx_buffer = 0; + u32 tx_ring_size = get_tx_ring_size(tx_ring); + // check curent hw index previous tx descriptor + u32 cur_index = cns3xxx_get_tx_hw_index(ring_num) - 1; + + tx_buffer = get_tx_buffer_by_index(tx_ring, cur_index); + + + //while (1) + for (i=0 ; i < tx_ring_size ; ++i) { + if (tx_buffer->tx_desc->cown == 1 && tx_buffer->skb) { + dev_kfree_skb_any(tx_buffer->skb); + tx_buffer->skb=0; + //tx_buffer->tx_desc->cown == 1; + } else { + break; + } + // --tx_desc_pair_ptr + --cur_index; + tx_buffer = get_tx_buffer_by_index(tx_ring, cur_index); + + } +#endif + return 0; +} + +void do_arl_lookup(void) +{ +} + +inline void assign_netdev(RXBuffer volatile *rx_buffer) +{ + RXDesc * rx_desc=0; +#ifdef CONFIG_SWITCH_BIG_ENDIAN + RXDesc tmp_rx_desc; + rx_desc = &tmp_rx_desc; + swap_rx_desc(rx_buffer->rx_desc, rx_desc); +#else + rx_desc = rx_buffer->rx_desc; +#endif + + +#if defined(CONFIG_CNS3XXX_PORT_BASE) || defined(CNS3XXX_VLAN_8021Q) + // sp: + // 0 - mac port 0 + // 1 - mac port 1 + // 4 - mac port 2 + + switch (rx_desc->sp) + { + case 0: + { + rx_buffer->skb->dev = PORT0_NETDEV; + break; + } + case 1: + { + rx_buffer->skb->dev = PORT1_NETDEV; + break; + } + case 4: + { + rx_buffer->skb->dev = PORT2_NETDEV; + break; + } + + } +#endif + +#ifdef CONFIG_CNS3XXX_VLAN_BASE +{ + u16 vlan_tag; + + vlan_tag = rx_desc->c_vid; + rx_buffer->skb->dev = net_dev_array[vlan_tag]; + +} +#endif + +} + +#if defined(CNS3XXX_VLAN_8021Q) +static int cns3xxx_vlan_rx(CNS3XXXPrivate *priv, struct sk_buff *skb, u16 vlan_tag) +{ + return vlan_hwaccel_receive_skb(skb, priv->vlgrp, vlan_tag); +} +#endif + +// old_priv has ring index information, current version only uses the information. +static int cns3xxx_get_rfd_buff(RXBuffer volatile *rx_buffer, CNS3XXXPrivate *old_priv) +{ + CNS3XXXPrivate *priv=0; + //RXDesc volatile *rxdesc_ptr = rx_buffer->rx_desc; + struct sk_buff *skb; + //unsigned char *data; + u32 len; + RXDesc *rx_desc; + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + + RXDesc tmp_rx_desc; + + rx_desc = &tmp_rx_desc; + swap_rx_desc(rx_buffer->rx_desc, rx_desc); + +#else + rx_desc = rx_buffer->rx_desc; +#endif + + //rxdesc_ptr = rxring.vir_addr + index; + skb = rx_buffer->skb; + len = rx_desc->sdl; + + +#ifdef DEBUG_RX + if (MSG_LEVEL == DUMP_RX_PKT_INFO) { + print_packet(skb->data, len); + } + +#endif + + pci_dma_sync_single_for_device(NULL, virt_to_phys(skb->data), len, PCI_DMA_FROMDEVICE); +#if defined (CONFIG_CNS3XXX_SPPE) + if (PACKET_REASON_TO_CPU == rx_buffer->rx_desc->hr) { + if (sppe_pci_fp_ready) { + SPPE_PARAM param; + int pci_dev_index; + struct iphdr *iph; + + skb_put(skb, len); + iph = (struct iphdr *)(skb->data + sizeof(struct ethhdr)); + + memset(¶m, 0, sizeof(SPPE_PARAM)); + param.cmd = SPPE_CMD_ARP; + param.op = SPPE_OP_GET; + param.data.sppe_arp.ip[0] = iph->daddr; + if (SPPE_RESULT_SUCCESS != sppe_func_hook(¶m)) { + goto NOT_IN_PCI_FP; + } else { + pci_dev_index = param.data.sppe_arp.unused_1; + } + param.cmd = SPPE_CMD_PCI_FP_DEV; + param.op = SPPE_OP_GET; + param.data.sppe_pci_fp_dev.dev = NULL; + param.data.sppe_pci_fp_dev.index = pci_dev_index; + if (SPPE_RESULT_SUCCESS != sppe_pci_fp_hook(¶m)) { + goto NOT_IN_PCI_FP; + } else { + skb->dev = param.data.sppe_pci_fp_dev.dev; + } + #if 1 + dev_queue_xmit(skb); + #else + skb->dev->hard_start_xmit(skb, skb->dev); + #endif + + return 0; + } + } +NOT_IN_PCI_FP: +#endif + +#ifdef CNS3XXX_NON_NIC_MODE_8021Q + if (cns3xxx_is_untag_packet(rx_desc) == 1) + take_off_vlan_header(skb); +#endif + +#ifdef CONFIG_CNS3XXX_PORT_BASE + assign_netdev(rx_buffer); + + if (rx_buffer->skb->dev) // if skb->dev is 0, means VLAN base + goto determine_dev_ok; + +#endif /* CONFIG_CNS3XXX_PORT_BASE */ + + +#ifdef CONFIG_CNS3XXX_VLAN_BASE + +#ifdef CONFIG_HAVE_VLAN_TAG + +#if defined(CNS3XXX_VLAN_8021Q) + // some funcion need netdev like eth_type_trans(), so need to assign it. + skb->dev = intr_netdev; + // 8021Q module will determine right netdev by vlan tag. +#else // defined(CNS3XXX_VLAN_8021Q) + { + assign_netdev(rx_buffer); + + take_off_vlan_header(skb); + if (MSG_LEVEL == 5) + print_packet(skb->data, 32); + + if ( rx_buffer->skb->dev == 0){ + goto freepacket; + } + } + +#endif // CNS3XXX_VLAN_8021Q + +#else /* CONFIG_HAVE_VLAN_TAG */ + +#ifdef CNS3XXX_RX_DESC_VLAN_INFO +// get VLAN information by RX descriptor field + +#endif + +#endif // CONFIG_HAVE_VLAN_TAG + +#endif // CONFIG_CNS3XXX_VLAN_BASE + + +#ifdef CONFIG_CNS3XXX_PORT_BASE +determine_dev_ok: +#endif + + skb_put(skb, len); + + if (skb->dev) { + priv = netdev_priv(skb->dev); + } + else{ + DEBUG_MSG(WARNING_MSG, "skb_ptr->dev==NULL\n"); + goto freepacket; + } + +#ifdef CNS3XXX_RX_HW_CHECKSUM + switch (rx_desc->prot) + { + case 1 : + case 2 : + case 5 : + case 6 : + { + if ( rx_desc->l4f == 0) { // tcp/udp checksum is correct + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else { + skb->ip_summed = CHECKSUM_NONE; + } + break; + } + default: + { + skb->ip_summed = CHECKSUM_NONE; + break; + } + } +#else + skb->ip_summed = CHECKSUM_NONE; +#endif // CNS3XXX_RX_HW_CHECKSUM + + + // this line must, if no, packet will not send to network layer +#ifdef CONFIG_FAST_BRIDGE + if (fast_bridge_en == 0) +#endif + skb->protocol = eth_type_trans(skb, skb->dev); + + skb->dev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes += len; + +#ifdef CONFIG_FAST_BRIDGE + if (fast_bridge_en == 1) { + + skb->ip_summed = CHECKSUM_NONE; + if ( skb->dev == PORT0_NETDEV) { + skb->dev = PORT1_NETDEV; + } else if ( skb->dev == PORT1_NETDEV) { + skb->dev = PORT0_NETDEV; + } + //skb->dev->hard_start_xmit(skb, skb->dev); + cns3xxx_send_packet(skb, skb->dev); + } else { +#endif // #ifdef CONFIG_FAST_BRIDGE + + +//#if defined(CNS3XXX_VLAN_8021Q) +#if 0 + if (priv->vlgrp != NULL) + { + //cns3xxx_vlan_rx(priv, skb, rx_buffer->rx_desc->c_vid); + cns3xxx_vlan_rx(priv, skb, rx_buffer->rx_desc->c_vid); + //cns3xxx_vlan_rx(priv, skb, swab16(le32_to_cpu(rx_buffer->rx_desc->c_vid)) ); + } + else +#else + #ifdef CONFIG_CNS3XXX_NAPI + netif_receive_skb(skb); + #else + netif_rx(skb); + #endif +#endif + +#ifdef CONFIG_FAST_BRIDGE + } +#endif + + //vlan_hwaccel_receive_skb(skb, priv->vlgrp, 1); + + return 0; + +freepacket: + //DEBUG_MSG(NORMAL_MSG, "freepacket\n"); + dev_kfree_skb_any(skb); + return 0; +} + +// index from 1 +inline u32 get_rx_hw_index(CNS3XXXPrivate *priv) +{ + return ((FS_DESC_PTR0_REG - get_rx_head_phy_addr(&RX_RING0(priv))) / sizeof(RXDesc) ); +} + +inline int get_rx_hw_index_by_reg(u8 ring_num) +{ + if (ring_num == 0 ) { + return ((FS_DESC_PTR0_REG - FS_DESC_BASE_ADDR0_REG) / sizeof(RXDesc) ); + } else if (ring_num == 1 ) { + return ((FS_DESC_PTR1_REG - FS_DESC_BASE_ADDR1_REG) / sizeof(RXDesc) ); + } + + return CAVM_FAIL; +} + +void dump_rxring(void) +{ + int j=0; + RXBuffer *rx_buffer = 0; + + rx_buffer = get_rx_ring_head(g_ring_info.rx_ring+0); + for (j=0 ; j < get_rx_ring_size(g_ring_info.rx_ring+0); ++j, ++rx_buffer) { + printk("[%d] ## rx_buffer->rx_desc->cown: %d\n", j, rx_buffer->rx_desc->cown); + } +} + +#ifdef CONFIG_CNS3XXX_NAPI +void cns3xxx_receive_packet(CNS3XXXPrivate *priv, int mode, int *work_done, int work_to_do) +#else +void cns3xxx_receive_packet(CNS3XXXPrivate *priv, int mode) +#endif +{ + int fssd_index; + //int fssd_current; + RXBuffer volatile *rx_buffer = 0; + RXDesc volatile *rx_desc=0; + struct sk_buff *skb; +#ifndef CONFIG_CNS3XXX_NAPI + int fsqf = 0; // Queue Full Mode =0 +#endif + int i, rxcount = 0; + u8 queue_index = priv->ring_index; + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + RXDesc tmp_rx_desc; +#endif + + rx_buffer = get_cur_rx_buffer(&(priv->rx_ring[queue_index])); + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + rx_desc = &tmp_rx_desc; + swap_rx_desc(rx_buffer->rx_desc, rx_desc); +#else + rx_desc = rx_buffer->rx_desc; +#endif + + fssd_index = get_rx_hw_index_by_reg(queue_index); + + if (fssd_index > get_rx_cur_index(&priv->rx_ring[queue_index]) ) { + rxcount = fssd_index - get_rx_cur_index(&priv->rx_ring[queue_index]); + } else if (fssd_index < get_rx_cur_index(&priv->rx_ring[queue_index])) { + rxcount = (get_rx_ring_size(&priv->rx_ring[queue_index]) - get_rx_cur_index(&priv->rx_ring[queue_index]) ) + fssd_index; + } else { // fssd_index == rxring.cur_index + if (rx_desc->cown == 0) { // if rx_desc->cown is 1, we can receive the RX descriptor. + enable_rx_dma(0, 1); + goto receive_packet_exit; + } else { + // Queue Full +#ifndef CONFIG_CNS3XXX_NAPI + fsqf = 1; +#endif + rxcount = get_rx_ring_size(&priv->rx_ring[queue_index]); + } + } +#ifndef CONFIG_CNS3XXX_NAPI + if (mode == 1) { + fsqf = 1; + rxcount = get_rx_ring_size(&priv->rx_ring[queue_index]); + } +#endif + +#ifdef CNS3XXX_FREE_TX_IN_RX_PATH + free_tx_desc_skb(priv->tx_ring + 0, 0); +#ifdef CNS3XXX_DOUBLE_TX_RING + free_tx_desc_skb(priv->tx_ring + 1, 1); +#endif +#endif + + for (i = 0; i < rxcount; i++) { + + if (rx_desc->cown != 0) { // start to get packet + // Alloc New skb_buff + skb = cns3xxx_alloc_skb(); + // Check skb_buff + if (skb) { + cns3xxx_get_rfd_buff(rx_buffer, priv); + rx_buffer->skb = skb; +#ifndef NCNB_TEST + rx_desc->sdp = (u32)virt_to_phys(skb->data); +#endif + rx_desc->sdl = MAX_PACKET_LEN; + rx_desc->fsd = 1; + rx_desc->lsd = 1; + rx_desc->cown = 0; // set cbit to 0 +#ifdef CONFIG_SWITCH_BIG_ENDIAN + swap_rx_desc(rx_desc, rx_buffer->rx_desc); +#endif + +#ifdef CONFIG_CNS3XXX_NAPI + ++(*work_done); + if (*work_done >= work_to_do) { + + rx_index_next(&priv->rx_ring[queue_index]); // rx_ring.cur_index points to next + rx_buffer = get_cur_rx_buffer(&priv->rx_ring[queue_index]); + rx_desc = rx_buffer->rx_desc; + break; + } +#endif + + } else { + // I will add dev->lp.stats->rx_dropped, it will effect the performance + //PDEBUG("%s: Alloc sk_buff fail, reuse the buffer\n", __FUNCTION__); + rx_desc->cown = 0; // set cbit to 0 +#ifdef CONFIG_SWITCH_BIG_ENDIAN + swap_rx_desc(rx_desc, rx_buffer->rx_desc); +#endif + + return; + } + } else { // cown is 0, no packets + //*work_done = 0; + return; + } + + + rx_index_next(&priv->rx_ring[queue_index]); // rx_ring.cur_index points to next + rx_buffer = get_cur_rx_buffer(&priv->rx_ring[queue_index]); + rx_desc = rx_buffer->rx_desc; + + } // end for (i = 0; i < rxcount; i++) + + +#ifndef CONFIG_CNS3XXX_NAPI + if (fsqf) { + priv->rx_ring[queue_index].cur_index = fssd_index; + mb(); + enable_rx_dma(0, 1); + } +#endif + + + //spin_unlock(&rx_lock); +receive_packet_exit: + return; +} + +irqreturn_t cns3xxx_fsrc_ring0_isr(int irq, void *dev_id) +{ + struct net_device *netdev = dev_id; + CNS3XXXPrivate *priv = netdev_priv(netdev); + + priv->ring_index=0; + +#ifdef CONFIG_CNS3XXX_NAPI +{ + CNS3XXXPrivate *priv = netdev_priv(napi_dev); + priv->ring_index=0; + +#ifdef CNS3XXX_USE_MASK + cns3xxx_write_pri_mask(0xb0); +#else + cns3xxx_disable_irq(FSRC_RING0_INTERRUPT_ID); +#endif + + //if (likely(netif_rx_schedule_prep(napi_dev, &priv->napi))) { + if (likely(napi_schedule_prep(&priv->napi))) { + //__netif_rx_schedule(napi_dev, &priv->napi); + __napi_schedule(&priv->napi); + } else { +#ifdef CNS3XXX_USE_MASK + cns3xxx_write_pri_mask(0xf0); +#else + cns3xxx_enable_irq(FSRC_RING0_INTERRUPT_ID); +#endif + } +} +#else // !CONFIG_CNS3XXX_NAPI + +#ifdef CNS3XXX_USE_MASK + cns3xxx_write_pri_mask(0xb0); +#else + cns3xxx_disable_irq(FSRC_RING0_INTERRUPT_ID); + cns3xxx_disable_irq(FSQF_RING0_INTERRUPT_ID); +#endif + + cns3xxx_receive_packet(priv, 0); // Receive Once + +#ifdef CNS3XXX_USE_MASK + cns3xxx_write_pri_mask(0xf0); +#else + cns3xxx_enable_irq(FSRC_RING0_INTERRUPT_ID); + cns3xxx_enable_irq(FSQF_RING0_INTERRUPT_ID); +#endif + enable_rx_dma(0, 1); +#endif + + return IRQ_HANDLED; +} + + +#if defined(CNS3XXX_DOUBLE_RX_RING) +irqreturn_t cns3xxx_fsrc_ring1_isr(int irq, void *dev_id) +{ + struct net_device *netdev = dev_id; + CNS3XXXPrivate *priv = netdev_priv(netdev); + priv->ring_index=1; + + +#if defined(CONFIG_CNS3XXX_NAPI) && defined(CNS3XXX_DOUBLE_RX_RING) +{ + CNS3XXXPrivate *priv = netdev_priv(r1_napi_dev); + priv->ring_index=1; + + cns3xxx_disable_irq(FSRC_RING1_INTERRUPT_ID); + + if (likely(napi_schedule_prep(&priv->napi))) { + __napi_schedule(&priv->napi); + } else { + cns3xxx_enable_irq(FSRC_RING1_INTERRUPT_ID); + } +} +#else + + cns3xxx_disable_irq(CNS3XXX_FSRC_RING1_INTERRUPT_ID); + cns3xxx_disable_irq(CNS3XXX_FSQF_RING1_INTERRUPT_ID); + cns3xxx_receive_packet(priv, 0); // Receive Once + enable_rx_dma(1, 1); + + cns3xxx_enable_irq(CNS3XXX_FSRC_RING1_INTERRUPT_ID); + cns3xxx_enable_irq(CNS3XXX_FSQF_RING1_INTERRUPT_ID); +#endif + + return IRQ_HANDLED; +} +#endif + +int cns3xxx_check_enough_tx_descriptor(TXRing *tx_ring, int need_free_tx_desc) +{ +#if 1 + int i=0; + TXDesc *tx_desc=0; + u32 cur_index = get_tx_cur_index(tx_ring); + TXBuffer *tx_buffer = get_tx_buffer_by_index(tx_ring, cur_index); + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + TXDesc tmp_tx_desc; + tx_desc = &tmp_tx_desc; + swap_tx_desc(tx_buffer->tx_desc, tx_desc); +#else + tx_desc = tx_buffer->tx_desc; +#endif + + + for (i=0 ; i < need_free_tx_desc ; ++i) { + if ( tx_desc->cown == 0 ) { + return 0; // no free TX descriptor + } + tx_buffer = get_tx_buffer_by_index(tx_ring, ++cur_index); + } +#endif + return 1; +} + +// if return CAVM_ERR, means pad is fail, the packet cannot send by switch. + +int fill_a_skb_to_tx_desc(TXBuffer * tx_buffer, u8 *data, int len, struct sk_buff *skb, const struct CNS3XXXPrivate_ *priv, int sg, int fsd, int lsd) +{ + //TXDesc *tx_desc_ptr = tx_buffer->tx_desc; + static int tt=0; + + TXDesc *tx_desc_ptr = 0; +#ifdef CONFIG_SWTICH_BIG_ENDIAN + TXDesc tmp_tx_desc; + tx_desc_ptr = &tmp_tx_desc; + swap_tx_desc(tx_buffer->tx_desc, tx_desc_ptr); +#else + tx_desc_ptr = tx_buffer->tx_desc; +#endif + + + + if (tx_buffer->skb) { + dev_kfree_skb_any(tx_buffer->skb); + tx_buffer->skb = 0 ; + } else { + //++tx_ring.non_free_tx_skb; + } + + tx_buffer->skb = skb; /* for free skb */ + tx_desc_ptr->sdp = virt_to_phys(data); + tx_buffer->j = tt; + tx_buffer->tx_index = cns3xxx_get_tx_hw_index(0); + ++tt; + +#if 0 + { + static u16 previous_sn_num=10; + u16 sn_num=0; + u16 e_type=0; + + memcpy(&e_type, skb->data + 12, 2); + e_type = be16_to_cpu(e_type); + + if (e_type == 0x0800) { + memcpy(&sn_num, skb->data + 0x28, 2); + sn_num = be16_to_cpu(sn_num); + + if ( previous_sn_num == sn_num) + printk("dup\n"); + + previous_sn_num = sn_num; + } + + } +#endif + + +#ifdef CNS3XXX_TX_HW_CHECKSUM + tx_desc_ptr->ico = 1; + tx_desc_ptr->uco = 1; + tx_desc_ptr->tco = 1; +#else + tx_desc_ptr->ico = 0; + tx_desc_ptr->uco = 0; + tx_desc_ptr->tco = 0; +#endif + // Wake interrupt +#ifdef CNS3XXX_TSTC_RING0_ISR + tx_desc_ptr->interrupt = 1; +#else + tx_desc_ptr->interrupt = 0; +#endif + + /* fill 0 to MIN_PACKET_LEN size */ + // can change MIN_PACKET_LEN to 14 + if (sg==0 && len < MIN_PACKET_LEN) { + if (skb_padto(skb, MIN_PACKET_LEN)) + return CAVM_ERR; + + //memset(skb->data + len, 0, MIN_PACKET_LEN - len); + //skb->len = MIN_PACKET_LEN; + tx_desc_ptr->sdl = MIN_PACKET_LEN; + } else { + tx_desc_ptr->sdl = len; + } + + dma_cache_maint(data, tx_desc_ptr->sdl, PCI_DMA_TODEVICE); + + /* VLAN base or port base function to set TX descriptor */ + /* reference: tx_//port_base(), tx_vlan_base() */ + priv->net_device_priv->tx_func(tx_desc_ptr, priv, skb); + tx_desc_ptr->fsd = fsd; + tx_desc_ptr->lsd = lsd; + + /* NOT SG packet */ + if( fsd == 1 && lsd == 1) + tx_desc_ptr->cown = 0; + +#ifdef CONFIG_SWITCH_BIG_ENDIAN + swap_tx_desc(tx_desc_ptr, tx_buffer->tx_desc); +#endif + + return CAVM_OK; +} + +int cns3xxx_send_packet(struct sk_buff *skb, struct net_device *netdev) +{ + + CNS3XXXPrivate *priv = netdev_priv(netdev); + TXBuffer *tx_buffer = 0; + unsigned long flags; + int nr_frags =skb_shinfo(skb)->nr_frags; + + TXDesc *tx_desc[10]; // FIXME: ensure to maximum sg size + int tx_desc_count=0; + int i=0; + +#ifdef DEBUG_TX + if (MSG_LEVEL == DUMP_TX_PKT_INFO) { + print_packet(tx_buffer->skb->data, tx_buffer->tx_desc->sdl); + //dump_tx_desc(tx_buffer->tx_desc); + } +#endif + + spin_lock_irqsave(&tx_lock, flags); + + if (cns3xxx_check_enough_tx_descriptor(priv->tx_ring + ring_index, (nr_frags==0 ) ? 1 : nr_frags) == 0) { + // no enough tx descriptor + spin_unlock_irqrestore(&tx_lock, flags); + // re-queue the skb + return NETDEV_TX_BUSY; + } + + tx_buffer = get_cur_tx_buffer(priv->tx_ring + ring_index); + + if (nr_frags == 0) { // non scatter/gather I/O + + fill_a_skb_to_tx_desc(tx_buffer, skb->data, skb->len, skb, priv, 0, 1, 1); + + tx_index_next(priv->tx_ring + ring_index); + + } else { // scatter/gather I/O + struct skb_frag_struct *frag = 0; + + + fill_a_skb_to_tx_desc(tx_buffer, skb->data, skb->len - skb->data_len, 0, priv, 1, 1, 0); + tx_desc[tx_desc_count++] = tx_buffer->tx_desc; + tx_index_next(priv->tx_ring + ring_index); + tx_buffer = get_cur_tx_buffer(priv->tx_ring + ring_index); + + for (i=0 ; i < nr_frags-1 ; ++i) { + frag = &skb_shinfo(skb)->frags[i]; + + fill_a_skb_to_tx_desc(tx_buffer, page_address(frag->page) + frag->page_offset, frag->size, 0, priv, 1, 0, 0); + tx_desc[tx_desc_count++] = tx_buffer->tx_desc; + + tx_index_next(priv->tx_ring + ring_index); + tx_buffer = get_cur_tx_buffer(priv->tx_ring + ring_index); + } + frag = &skb_shinfo(skb)->frags[nr_frags-1]; + + // last fragment + fill_a_skb_to_tx_desc(tx_buffer, page_address(frag->page) + frag->page_offset, frag->size, skb, priv, 1, 0, 1); + tx_desc[tx_desc_count++] = tx_buffer->tx_desc; + + tx_index_next(priv->tx_ring + ring_index); + tx_buffer = get_cur_tx_buffer(priv->tx_ring + ring_index); + } + + + if( nr_frags != 0) { + + for (i = 0; i < tx_desc_count ; i++ ) + tx_desc[i]->cown = 0 ; + } + + mb(); + enable_tx_dma(ring_index, 1); + + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + netdev->trans_start = jiffies; + + spin_unlock_irqrestore(&tx_lock, flags); + return NETDEV_TX_OK; +} + + +#ifdef CNS3XXX_FSQF_RING0_ISR +irqreturn_t cns3xxx_fsqf_ring0_isr(int irq, void *dev_id) +{ +#ifndef CONFIG_CNS3XXX_NAPI + struct net_device *netdev = dev_id; + CNS3XXXPrivate *priv = netdev_priv(netdev); +#endif + +#ifdef CONFIG_CNS3XXX_NAPI +{ + CNS3XXXPrivate *priv = netdev_priv(napi_dev); + // because in normal state, fsql only invoke once and set_bit is atomic function. + // so I don't mask it. + set_bit(0, &priv->is_qf); +} +#else +#ifdef CNS3XXX_USE_MASK + cns3xxx_write_pri_mask(0xb0); +#else + cns3xxx_disable_irq(FSRC_RING0_INTERRUPT_ID); + cns3xxx_disable_irq(FSQF_RING0_INTERRUPT_ID); +#endif + + + cns3xxx_receive_packet(priv, 1); // Receive at Queue Full Mode + +#ifdef CNS3XXX_USE_MASK + cns3xxx_write_pri_mask(0xf0); +#else + cns3xxx_enable_irq(FSRC_RING0_INTERRUPT_ID); + cns3xxx_enable_irq(FSQF_RING0_INTERRUPT_ID); +#endif + + enable_rx_dma(0, 1); +#endif // CONFIG_CNS3XXX_NAPI + + return IRQ_HANDLED; +} +#endif + + +#if defined(CNS3XXX_DOUBLE_RX_RING) +#ifdef CNS3XXX_FSQF_RING1_ISR +irqreturn_t cns3xxx_fsqf_ring1_isr(int irq, void *dev_id) +{ + struct net_device *netdev = dev_id; + CNS3XXXPrivate *priv = netdev_priv(netdev); + //INTC_CLEAR_EDGE_TRIGGER_INTERRUPT(INTC_GSW_FSQF_BIT_INDEX); + +#ifdef CONFIG_CNS3XXX_NAPI +{ + CNS3XXXPrivate *priv = netdev_priv(r1_napi_dev); + // because in normal state, fsqf only invoke once and set_bit is atomic function. + // so don't mask it. + set_bit(0, &priv->is_qf); +} +#else + cns3xxx_disable_irq(FSRC_RING1_INTERRUPT_ID); + cns3xxx_disable_irq(FSQF_RING1_INTERRUPT_ID); + + cns3xxx_receive_packet(priv, 1); // Receive at Queue Full Mode + enable_rx_dma(1, 1); + + cns3xxx_enable_irq(FSRC_RING1_INTERRUPT_ID); + cns3xxx_enable_irq(FSQF_RING1_INTERRUPT_ID); +#endif + return IRQ_HANDLED; +} +#endif +#endif //#if defined(CNS3XXX_DOUBLE_RX_RING) + + +#ifdef CNS3XXX_STATUS_ISR +irqreturn_t cns3xxx_status_isr(int irq, void *dev_id) +{ + u32 int_status = INTR_STAT_REG; + u32 i=0; + + cns3xxx_disable_irq(STATUS_INTERRUPT_ID); + for (i = 0; i < 32; i++) { + if (int_status & (1 << i)) { + PRINT_INFO(cns3xxx_gsw_status_tbl[i]); + } + } + INTR_STAT_REG = 0xffffffff; // write 1 for clear. + cns3xxx_enable_irq(STATUS_INTERRUPT_ID); + return IRQ_HANDLED; +} +#endif + + +#ifdef CNS3XXX_TSTC_RING0_ISR +irqreturn_t cns3xxx_tstc_ring0_isr(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} +#endif + + +static int cns3xxx_install_isr(struct net_device *dev) +{ + int retval; + CNS3XXXPrivate *priv = netdev_priv(dev); + + if (install_isr_rc == 0) { + + retval = request_irq(FSRC_RING0_INTERRUPT_ID, cns3xxx_fsrc_ring0_isr, IRQF_SHARED, "FSRC_RING0", intr_netdev); + + if (retval) { + return 1; + } + +#ifdef CNS3XXX_FSQF_RING0_ISR + retval = request_irq(FSQF_RING0_INTERRUPT_ID, cns3xxx_fsqf_ring0_isr, IRQF_SHARED, "FSQF_RING0", intr_netdev); + + if (retval) { + PRINT_INFO("%s: unable to get IRQ %d (irqval=%d).\n", "FSQF_RING0", FSQF_RING0_INTERRUPT_ID, retval); + return 2; + } +#endif + +#ifdef CNS3XXX_TSTC_RING0_ISR + retval = request_irq(TSTC_RING0_INTERRUPT_ID, cns3xxx_tstc_ring0_isr, IRQF_SHARED, "TSTC_RING0", intr_netdev); + + if (retval) { + PRINT_INFO("%s: unable to get IRQ %d (irqval=%d).\n", "TSTC_RING0", FSQF_RING0_INTERRUPT_ID, retval); + return 3; + } + +#endif + + + if (priv->num_rx_queues == 2) { +#if defined(CNS3XXX_DOUBLE_RX_RING) + retval = request_irq(FSRC_RING1_INTERRUPT_ID, cns3xxx_fsrc_ring1_isr, IRQF_SHARED, "FSRC_RING1", intr_netdev); + + if (retval) { + return 1; + } + +#ifdef CNS3XXX_FSQF_RING1_ISR + retval = request_irq(FSQF_RING1_INTERRUPT_ID, cns3xxx_fsqf_ring1_isr, IRQF_SHARED, "FSQF_RING1", intr_netdev); + + if (retval) { + PRINT_INFO("%s: unable to get IRQ %d (irqval=%d).\n", "FSQF_RING1", FSQF_RING1_INTERRUPT_ID, retval); + return 2; + } +#endif + +#endif + } + +#ifdef CNS3XXX_STATUS_ISR + retval = request_irq(STATUS_INTERRUPT_ID, cns3xxx_status_isr, IRQF_SHARED, "GSW_STATUS", intr_netdev); + + if (retval) { + PRINT_INFO("%s: unable to get IRQ %d (irqval=%d).\n", "GSW STATUS INT", STATUS_INTERRUPT_ID, retval); + return 3; + } + INTR_MASK_REG = 0; +#endif + + + + + + +#ifdef CONFIG_CNS3XXX_NAPI +{ + CNS3XXXPrivate *sp = netdev_priv(napi_dev); + napi_enable(&sp->napi); + netif_start_queue(napi_dev); + +#ifdef CNS3XXX_DOUBLE_RX_RING + sp = netdev_priv(r1_napi_dev); + napi_enable(&sp->napi); + netif_start_queue(r1_napi_dev); +#endif +} +#endif + // enable cpu port + enable_port(3, 1); + + } // end if (install_isr_rc == 0) + + ++install_isr_rc; + + return 0; +} + + +int cns3xxx_open(struct net_device *dev) +{ + CNS3XXXPrivate *priv = netdev_priv(dev); + //static int init_state=0; + + if (cns3xxx_setup_rx_tx_res(priv) != CAVM_OK) { + return -1; + } + + netif_start_queue(dev); + priv->net_device_priv->open(); + + cns3xxx_install_isr(dev); + + enable_rx_dma(0, 1); + + if (priv->num_rx_queues == 2) + enable_rx_dma(1, 1); + + netif_carrier_on(dev); + + return 0; +} + +static int cns3xxx_uninstall_isr(struct net_device *dev) +{ + CNS3XXXPrivate *priv = netdev_priv(dev); + --install_isr_rc; + if (install_isr_rc == 0) { + enable_port(3, 0); + free_irq(FSRC_RING0_INTERRUPT_ID, intr_netdev); +#ifdef CNS3XXX_STATUS_ISR + free_irq(STATUS_INTERRUPT_ID, intr_netdev); +#endif + +#ifdef CNS3XXX_FSQF_RING0_ISR + free_irq(FSQF_RING0_INTERRUPT_ID, intr_netdev); +#endif + +#ifdef CNS3XXX_TSTC_RING0_ISR + free_irq(TSTC_RING0_INTERRUPT_ID, intr_netdev); +#endif + + if (priv->num_rx_queues == 2) { + free_irq(FSRC_RING1_INTERRUPT_ID, intr_netdev); + +#ifdef CNS3XXX_FSQF_RING1_ISR + free_irq(FSQF_RING1_INTERRUPT_ID, intr_netdev); +#endif + } + + + +#ifdef CONFIG_CNS3XXX_NAPI +{ + CNS3XXXPrivate *sp = netdev_priv(napi_dev); + + napi_disable(&sp->napi); + netif_stop_queue(napi_dev); +#ifdef CNS3XXX_DOUBLE_RX_RING + sp = netdev_priv(r1_napi_dev); + + napi_disable(&sp->napi); + netif_stop_queue(r1_napi_dev); +#endif +} +#endif + + + } + + return 0; +} + +int cns3xxx_close(struct net_device *dev) +{ + CNS3XXXPrivate *priv = netdev_priv(dev); + + enable_rx_dma(0, 0); + enable_tx_dma(0, 0); + + if (priv->num_rx_queues == 2) + enable_tx_dma(1, 0); + + if (priv->num_tx_queues == 2) + enable_rx_dma(1, 0); + + netif_stop_queue(dev); + + priv->net_device_priv->close(); + cns3xxx_uninstall_isr(dev); + cns3xxx_free_rx_tx_res(priv); + netif_carrier_off(dev); + return 0; +} + + + +//#define MAC_PORT(p) MAC##p##_CFG_REG + +void broadcast_storm_cfg(u8 port, u8 boradcast, u8 multicast, u8 unknown) +{ + switch (port) + { + case 0: + { + (boradcast == 1) ? (MAC0_CFG_REG |= (1 << 30)) : (MAC0_CFG_REG &= (~(1 << 30))) ; + (multicast == 1) ? (MAC0_CFG_REG |= (1 << 29)) : (MAC0_CFG_REG &= (~(1 << 29))) ; + (unknown == 1) ? (MAC0_CFG_REG |= (1 << 28)) : (MAC0_CFG_REG &= (~(1 << 28))) ; + break; + } + case 1: + { + (boradcast == 1) ? (MAC1_CFG_REG |= (1 << 30)) : (MAC1_CFG_REG &= (~(1 << 30))) ; + (multicast == 1) ? (MAC1_CFG_REG |= (1 << 29)) : (MAC1_CFG_REG &= (~(1 << 29))) ; + (unknown == 1) ? (MAC1_CFG_REG |= (1 << 28)) : (MAC1_CFG_REG &= (~(1 << 28))) ; + break; + } + case 2: + { + (boradcast == 1) ? (MAC2_CFG_REG |= (1 << 30)) : (MAC2_CFG_REG &= (~(1 << 30))) ; + (multicast == 1) ? (MAC2_CFG_REG |= (1 << 29)) : (MAC2_CFG_REG &= (~(1 << 29))) ; + (unknown == 1) ? (MAC2_CFG_REG |= (1 << 28)) : (MAC2_CFG_REG &= (~(1 << 28))) ; + break; + } + } +} + +void broadcast_storm_rate(u8 rate) +{ + TC_CTRL_REG &= (~(0xf << 24)); + TC_CTRL_REG |= (rate << 24); +} + +// port: 0, 1, 2 ; port0, port1 and port2 +// config general mac port configuration +void cns3xxx_general_mac_cfg(u8 port) +{ + u32 cfg=0; + + switch (port) + { + case 0: + { + cfg = MAC0_CFG_REG; + break; + } + case 1: + { + cfg = MAC1_CFG_REG; + break; + } + case 2: + { + cfg = MAC2_CFG_REG; + break; + } + } + + + // txc_check_en: 1 + cfg |= (1 << 13); + + // bp_en: 1 + cfg |= (1 << 17); + +#ifdef CNS3XXX_LEARN_ENABLE + // learn_dis: 0 + cfg &= (~(1 << 19)); +#else + // learn disable + cfg |= (1 << 19); +#endif + + // blocking_state: 0 + cfg &= (~(1 << 20)); + + // block_mode: 0 + cfg &= (~(1 << 21)); + +#ifdef CNS3XXX_AGE_ENABLE + // age_en: 1 + cfg |= (1 << 22); + +#else + // age disable + cfg &= (~(1 << 22)); +#endif + + // SA_secured: 0 + cfg &= (~(1 << 23)); + + switch (port) + { + case 0: + { + MAC0_CFG_REG = cfg; + break; + } + case 1: + { + MAC1_CFG_REG = cfg; + break; + } + case 2: + { + MAC2_CFG_REG = cfg; + break; + } + } + +} + +void cns3xxx_configu_cpu_port(void) +{ + // Set CPU port to general configuration + +#ifdef CNS3XXX_LEARN_ENABLE + CPU_CFG_REG &= (~(1 << 19)); +#else + // learn_dis: 1 + CPU_CFG_REG |= (1 << 19); +#endif + +#ifdef CNS3XXX_AGE_ENABLE + // age_en: 1 + CPU_CFG_REG |= (1 << 22); +#else + // age disable + CPU_CFG_REG &= (~(1 << 22)); +#endif + + // SA_secured: 0 + CPU_CFG_REG &= (~(1 << 23)); + + // go to hnat:1 + CPU_CFG_REG |= (1 << 29); + + //offset 4N +2 + CPU_CFG_REG &= (~(1 << 30)); +#ifdef CNS3XXX_4N + CPU_CFG_REG |= (1 << 30); +#endif + + // cpu flow control disable + CPU_CFG_REG &= (~(1 << 31)); +#ifdef CNS3XXX_CPU_PORT_FC + // cpu flow control enable + CPU_CFG_REG |= (1 << 31); +#endif + +} + +static void __init cns3xxx_gsw_hw_init(void) +{ + //u32 mac_port_config; + int i; + //u32 cfg_reg = 0; + u32 reg_config = 0; + +#ifdef CONFIG_SILICON + + //GPIOB_PIN_EN_REG |= (1 << 14); //enable GMII2_CRS + //GPIOB_PIN_EN_REG |= (1 << 15); //enable GMII2_COL + GPIOB_PIN_EN_REG |= (1 << 20); //enable MDC + GPIOB_PIN_EN_REG |= (1 << 21); //enable MDIO + + cns3xxx_gsw_power_enable(); + cns3xxx_gsw_software_reset(); +#endif + +#ifdef CNS3XXX_CONFIG_SIM_MODE + SLK_SKEW_CTRL_REG |= (1 << 31); +#endif + + +#if 1 + while (((SRAM_TEST_REG >> 20) & 1) == 0); +#endif + + clear_fs_dma_state(1); + + + // disable port mac0, mac1, mac2, cpu port + enable_port(0, 0); + enable_port(1, 0); + enable_port(2, 0); + enable_port(3, 0); + + // disable RX0/TX0 RX1/TX1 DMA + enable_tx_dma(0, 0); + enable_tx_dma(1, 0); + enable_rx_dma(0, 0); + enable_rx_dma(1, 0); + + INTR_STAT_REG = 0xffffffff; // write 1 for clear. + +#ifdef CNS3XXX_DELAYED_INTERRUPT + DELAY_INTR_CFG_REG = (1 << 16) | (max_pend_int_cnt << 8) | (max_pend_time); +#endif + + reg_config = PHY_AUTO_ADDR_REG; + reg_config &= ~(3 << 30); +#ifdef CONFIG_CNS3XXX_JUMBO_FRAME + reg_config |= (3 << 30); // maximum frame length: 9600 bytes +#else + reg_config |= (2 << 30); // maximum frame length: 1536 bytes +#endif + + PHY_AUTO_ADDR_REG = reg_config; + + + // Set general value for MAC_GLOB_CFG_REG + // age_time: 2 ^(1-1) * 300 sec + MAC_GLOB_CFG_REG &= (~0xf); + MAC_GLOB_CFG_REG |= 1; + + + // bkoff_mode: 111 follow standard + MAC_GLOB_CFG_REG &= (~(0x7 << 9)); + MAC_GLOB_CFG_REG |= (0x7 << 9); + + // jam_no: 1010: + MAC_GLOB_CFG_REG &= (~(0xf << 12)); + MAC_GLOB_CFG_REG |= (0xa << 12); + + // bp_mode: 10: + MAC_GLOB_CFG_REG &= (~(0x3 << 16)); + MAC_GLOB_CFG_REG |= (0x2 << 16); + + // res_mc_flt: 0 + MAC_GLOB_CFG_REG &= (~(0x1 << 28)); + + // col_mode: 11 + MAC_GLOB_CFG_REG &= (~(0x3 << 18)); + MAC_GLOB_CFG_REG |= (0x3 << 18); + + // crc_stripping: 1 + MAC_GLOB_CFG_REG |= (0x1 << 20); + + + // ACCEPT_CRC_BAD_PKT : 0 + MAC_GLOB_CFG_REG &= (~(0x1 << 21)); + +#ifdef ACCEPT_CRC_BAD_PKT + MAC_GLOB_CFG_REG |= (0x1 << 21); +#endif + + // SVL + MAC_GLOB_CFG_REG &= (~(0x1 << 7)); + +#ifdef IVL + // IVL: 1 (IVL), 0 (SVL) + MAC_GLOB_CFG_REG |= (0x1 << 7); +#endif + + + // HNAT_en: 0 + MAC_GLOB_CFG_REG &= (~(0x1 << 26)); + + // Firewall_mode: 0 + MAC_GLOB_CFG_REG &= (~(0x1 << 27)); + + + + cns3xxx_general_mac_cfg(0); + cns3xxx_general_mac_cfg(1); + cns3xxx_general_mac_cfg(2); + cns3xxx_configu_cpu_port(); + + // write vlan table + // set cpu port vlan table + cns3xxx_vlan_table_add(&cpu_vlan_table_entry); + for (i=0 ; i < sizeof(vlan_table_entry)/sizeof(VLANTableEntry) ; ++i) + cns3xxx_vlan_table_add(&vlan_table_entry[i]); + + cns3xxx_set_pvid(0, PORT0_PVID); + cns3xxx_set_pvid(1, PORT1_PVID); + cns3xxx_set_pvid(2, PORT2_PVID); + cns3xxx_set_pvid(3, CPU_PVID); + +#ifdef CNS3XXX_SET_ARL_TABLE + // set arl table + cns3xxx_arl_table_flush(); +#endif +} + +static int cns3xxx_set_mac_addr(struct net_device *dev, void *p) +{ + //struct sockaddr *sock_addr = addr; + CNS3XXXPrivate *priv = netdev_priv(dev); + + struct sockaddr *addr= p; + + + spin_lock_irq(&priv->lock); + + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + // 1. delete old arl mac entry + // 2. add new arl mac entry + // 3. copy new mac to netdev field + + if (priv->net_device_priv->arl_table_entry) { + cns3xxx_arl_table_invalid(priv->net_device_priv->arl_table_entry); + memcpy(priv->net_device_priv->arl_table_entry->mac, addr->sa_data, dev->addr_len); + //print_arl_table_entry(priv->net_device_priv->arl_table_entry); + cns3xxx_arl_table_add(priv->net_device_priv->arl_table_entry); + } + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + spin_unlock_irq(&priv->lock); + return 0; +} + + +int set_fc_rls(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + FC_GLOB_THRS_REG &= (~(0x1ff << 16)); + FC_GLOB_THRS_REG |= (ctl.val << 16); + return CAVM_OK; +} + +int get_fc_rls(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + + ctl.val = ((FC_GLOB_THRS_REG >> 16) & 0x1ff); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + return CAVM_OK; +} + +int set_fc_set(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + FC_GLOB_THRS_REG &= (~0x1ff); + FC_GLOB_THRS_REG |= ctl.val; + return CAVM_OK; +} + +int get_fc_set(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + + ctl.val = ((FC_GLOB_THRS_REG) & 0x1ff); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + return CAVM_OK; +} + + +int set_sarl_rls(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + SARL_CTRL_REG &= (~(0x1ff << 12)); + SARL_CTRL_REG |= (ctl.val << 12); + return CAVM_OK; +} + +int get_sarl_rls(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + + ctl.val = ((SARL_CTRL_REG >> 12) & 0x1ff); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + return CAVM_OK; +} + +int set_sarl_enable(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + SARL_CTRL_REG &= (~(0x1 << 31)); + SARL_CTRL_REG |= (ctl.val << 31); + return CAVM_OK; +} + +int get_sarl_enable(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + ctl.val = ((SARL_CTRL_REG >> 31 ) & 0x1); + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + return CAVM_OK; +} +int set_sarl_set(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + SARL_CTRL_REG &= (~0x1ff); + SARL_CTRL_REG |= ctl.val; + return CAVM_OK; +} + +int get_sarl_set(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + + ctl.val = ((SARL_CTRL_REG) & 0x1ff); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + return CAVM_OK; +} + +int set_sarl_oq(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + + switch (ctl.gyr) + { + case 0: // green + { + SARL_OQ_GTH_REG &= (~(0xff << ctl.tc*8)); + SARL_OQ_GTH_REG |= (ctl.val << ctl.tc*8); + break; + } + case 1: // yellow + { + SARL_OQ_YTH_REG &= (~(0xff << ctl.tc*8)); + SARL_OQ_YTH_REG |= (ctl.val << ctl.tc*8); + break; + } + case 2: // red + { + SARL_OQ_RTH_REG &= (~(0xff << ctl.tc*8)); + SARL_OQ_RTH_REG |= (ctl.val << ctl.tc*8); + break; + } + } + return CAVM_OK; +} + +int get_sarl_oq(struct ifreq *ifr) +{ + CNS3XXXSARLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + + switch (ctl.gyr) + { + case 0: // green + { + ctl.val = ((SARL_OQ_GTH_REG >> ctl.tc*8) & 0xff); + break; + } + case 1: // yellow + { + ctl.val = ((SARL_OQ_YTH_REG >> ctl.tc*8) & 0xff); + break; + } + case 2: // red + { + ctl.val = ((SARL_OQ_RTH_REG >> ctl.tc*8) & 0xff); + break; + } + } + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXSARLEntry)) ) + return -EFAULT; + return CAVM_OK; +} + +int set_queue_weight(struct ifreq *ifr) +{ + CNS3XXXQueueWeightEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXQueueWeightEntry)) ) + return -EFAULT; + switch (ctl.which_port) + { + case 0: + { + QUEUE_WEIGHT_SET(0, ctl) + return 0; + } + case 1: + { + QUEUE_WEIGHT_SET(1, ctl) + return 0; + } + case 2: + { + QUEUE_WEIGHT_SET(2, ctl) + return 0; + } + case 3: // cpu port + { + CPU_PRI_CTRL_REG &= ~(0x3ffff); + CPU_PRI_CTRL_REG |= (ctl.sch_mode << 16); + CPU_PRI_CTRL_REG |= (ctl.q0_w); + CPU_PRI_CTRL_REG |= (ctl.q1_w << 4); + CPU_PRI_CTRL_REG |= (ctl.q2_w << 8); + CPU_PRI_CTRL_REG |= (ctl.q3_w << 12); + return 0; + } + case 4: // PPE port + { + HNAT_PRI_CTRL_REG &= ~(0x3ffff); + HNAT_PRI_CTRL_REG |= (ctl.sch_mode << 16); + HNAT_PRI_CTRL_REG |= (ctl.q0_w); + HNAT_PRI_CTRL_REG |= (ctl.q1_w << 4); + HNAT_PRI_CTRL_REG |= (ctl.q2_w << 8); + HNAT_PRI_CTRL_REG |= (ctl.q3_w << 12); + return 0; + } + default: + { + return -EFAULT; + } + } +} + +int get_queue_weight(struct ifreq *ifr) +{ + CNS3XXXQueueWeightEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXQueueWeightEntry)) ) + return -EFAULT; + + switch (ctl.which_port) + { + case 0: + { + QUEUE_WEIGHT_GET(0, ctl) + break; + } + case 1: + { + QUEUE_WEIGHT_GET(1, ctl) + break; + } + case 2: + { + QUEUE_WEIGHT_GET(2, ctl) + break; + } + case 3: + { + ctl.sch_mode = ((CPU_PRI_CTRL_REG >> 16 ) & 0x3); + ctl.q0_w = ((CPU_PRI_CTRL_REG >> 0 ) & 0x7); + ctl.q1_w = ((CPU_PRI_CTRL_REG >> 4 ) & 0x7); + ctl.q2_w = ((CPU_PRI_CTRL_REG >> 8 ) & 0x7); + ctl.q3_w = ((CPU_PRI_CTRL_REG >> 12 ) & 0x7); + break; + } + case 4: + { + ctl.sch_mode = ((HNAT_PRI_CTRL_REG >> 16 ) & 0x3); + ctl.q0_w = ((HNAT_PRI_CTRL_REG >> 0 ) & 0x7); + ctl.q1_w = ((HNAT_PRI_CTRL_REG >> 4 ) & 0x7); + ctl.q2_w = ((HNAT_PRI_CTRL_REG >> 8 ) & 0x7); + ctl.q3_w = ((HNAT_PRI_CTRL_REG >> 12 ) & 0x7); + break; + } + } + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXQueueWeightEntry)) ) + return -EFAULT; + + return CAVM_OK; +} + +int set_rate_limit(struct ifreq *ifr) +{ + CNS3XXXRateLimitEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXRateLimitEntry)) ) + return -EFAULT; + switch (ctl.which_port) + { + case 0: + { + RATE_CTRL_REG &= (~(0x7f << 8)); + RATE_CTRL_REG |= ( ctl.band_width << 8); + RATE_CTRL_REG &= (~(0x3)); + RATE_CTRL_REG |= ctl.base_rate; + return 0; + } + case 1: + { + RATE_CTRL_REG &= (~(0x7f << 16)); + RATE_CTRL_REG |= ( ctl.band_width << 16); + RATE_CTRL_REG &= (~(0x3 << 2)); + RATE_CTRL_REG |= (ctl.base_rate << 2); + return 0; + } + case 2: + { + RATE_CTRL_REG &= (~(0x7f << 24)); + RATE_CTRL_REG |= ( ctl.band_width << 24); + RATE_CTRL_REG &= (~(0x3 << 4)); + RATE_CTRL_REG |= (ctl.base_rate << 4); + return 0; + } + case 3: // port 0 extra dma + { + TC_CTRL_REG &= (~0x7f); + TC_CTRL_REG |= ctl.band_width; + RATE_CTRL_REG &= (~(0x3 << 6)); + RATE_CTRL_REG |= (ctl.base_rate << 6); + return 0; + } + default: + { + return -EFAULT; + } + } +} + +int get_rate_limit(struct ifreq *ifr) +{ + CNS3XXXRateLimitEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXRateLimitEntry)) ) + return -EFAULT; + switch (ctl.which_port) + { + case 0: + { + ctl.band_width = (RATE_CTRL_REG >> 8) & 0x7f; + ctl.base_rate = RATE_CTRL_REG & 0x3; + break; + } + case 1: + { + ctl.band_width = (RATE_CTRL_REG >> 16) & 0x7f; + ctl.base_rate = (RATE_CTRL_REG >> 2) & 0x3; + break; + } + case 2: + { + ctl.band_width = (RATE_CTRL_REG >> 24) & 0x7f; + ctl.base_rate = (RATE_CTRL_REG >> 4) & 0x3; + break; + } + case 3: // port 0 extra dma + { + ctl.band_width = (TC_CTRL_REG) & 0x7f; + ctl.base_rate = (RATE_CTRL_REG >> 6) & 0x3; + break; + } + default: + { + return -EFAULT; + } + } + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXRateLimitEntry)) ) + return -EFAULT; + + return CAVM_OK; +} + +int set_fc(struct ifreq *ifr) +{ + CNS3XXXFCEntry ctl; + u32 port_offset[]={0x0c, 0x10, 0x18, 0x14}; // 0x14 is cpu port offset + u32 val=0; + + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXFCEntry)) ) + return -EFAULT; + + val = SWITCH_REG_VALUE(port_offset[ctl.port]); + if (ctl.port == 3) { // cpu port, only can set rx fc + val &= (~(1 << 31)); + if (ctl.fc_en) + val |= (1 << 31); + } else { + val &= (~(1 << 11)); // disable rx fc + val &= (~(1 << 12)); // disable tx fc + val |= (ctl.fc_en << 11); + } + + SWITCH_REG_VALUE(port_offset[ctl.port]) = val; + return CAVM_OK; +} + +int get_fc(struct ifreq *ifr) +{ + CNS3XXXFCEntry ctl; + u32 port_offset[]={0x0c, 0x10, 0x18, 0x14}; // 0x14 is cpu port offset + u32 val=0; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXFCEntry)) ) + return -EFAULT; + + val = SWITCH_REG_VALUE(port_offset[ctl.port]); + if (ctl.port == 3) { // cpu port, only can set rx fc + ctl.fc_en = ((val >> 31) & 1); + } else { + ctl.fc_en = ((val >> 11) & 3); + + } + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXFCEntry)) ) + return -EFAULT; + + return CAVM_OK; +} + +int set_ivl(struct ifreq *ifr) +{ + CNS3XXXIVLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXIVLEntry)) ) + return -EFAULT; + + cns3xxx_ivl(ctl.enable); + + return CAVM_OK; +} + +int get_ivl(struct ifreq *ifr) +{ + CNS3XXXIVLEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXIVLEntry)) ) + return -EFAULT; + + ctl.enable = ((MAC_GLOB_CFG_REG >> 7) & 0x1); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXIVLEntry)) ) + return -EFAULT; + + return CAVM_OK; +} + +int set_wan_port(struct ifreq *ifr) +{ + CNS3XXXWANPortEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXWANPortEntry)) ) + return -EFAULT; + VLAN_CFG &= (~(0x1f << 8)); + VLAN_CFG |= (ctl.wan_port << 8); + + return CAVM_OK; +} +int get_wan_port(struct ifreq *ifr) +{ + CNS3XXXWANPortEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXWANPortEntry)) ) + return -EFAULT; + + ctl.wan_port = ((VLAN_CFG >> 8) & 0x1f); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXWANPortEntry)) ) + return -EFAULT; + + return CAVM_OK; +} + +int set_pvid(struct ifreq *ifr) +{ + CNS3XXXPVIDEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXPVIDEntry)) ) + return -EFAULT; + cns3xxx_set_pvid(ctl.which_port, ctl.pvid); + + return CAVM_OK; +} + +int get_pvid(struct ifreq *ifr) +{ + CNS3XXXPVIDEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXPVIDEntry)) ) + return -EFAULT; + + ctl.pvid = cns3xxx_get_pvid(ctl.which_port); + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXPVIDEntry)) ) + return -EFAULT; + return CAVM_OK; +} + +int set_qa(struct ifreq *ifr) +{ + CNS3XXXQAEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXQAEntry)) ) + return -EFAULT; + + MAC_GLOB_CFG_EXT_REG &= ~(0x7 << 27); + MAC_GLOB_CFG_EXT_REG |= (ctl.qa << 27); + + return CAVM_OK; +} + +int get_qa(struct ifreq *ifr) +{ + CNS3XXXQAEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXQAEntry)) ) + return -EFAULT; + + ctl.qa = (MAC_GLOB_CFG_EXT_REG >> 27) & 0x7; + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXQAEntry)) ) + return -EFAULT; + return CAVM_OK; +} + +int get_packet_max_len(struct ifreq *ifr) +{ + CNS3XXXMaxLenEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXMaxLenEntry)) ) + return -EFAULT; + + ctl.max_len = (PHY_AUTO_ADDR_REG >> 30) & 0x3; + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXMaxLenEntry)) ) + return -EFAULT; + return CAVM_OK; +} + +int set_packet_max_len(struct ifreq *ifr) +{ + CNS3XXXMaxLenEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXMaxLenEntry)) ) + return -EFAULT; + + PHY_AUTO_ADDR_REG &= (~(3 << 30)); + PHY_AUTO_ADDR_REG |= (ctl.max_len << 30); + + return CAVM_OK; +} + +int set_udp_range(struct ifreq *ifr) +{ + CNS3XXXUdpRangeEtypeControl conf; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXUdpRangeEtypeControl)) ) + return -EFAULT; + + switch (conf.udp_range_num) + { + case 0: + { + UDP_RANGE0_REG = 0; + UDP_RANGE0_REG |= conf.port_start; + UDP_RANGE0_REG |= (conf.port_end << 16); + break; + } + case 1: + { + UDP_RANGE1_REG = 0; + UDP_RANGE1_REG |= conf.port_start; + UDP_RANGE1_REG |= (conf.port_end << 16); + break; + } + case 2: + { + UDP_RANGE2_REG = 0; + UDP_RANGE2_REG |= conf.port_start; + UDP_RANGE2_REG |= (conf.port_end << 16); + break; + } + case 3: + { + UDP_RANGE3_REG = 0; + UDP_RANGE3_REG |= conf.port_start; + UDP_RANGE3_REG |= (conf.port_end << 16); + break; + } + } + + return CAVM_OK; +} + +int get_udp_range(struct ifreq *ifr) +{ + CNS3XXXUdpRangeEtypeControl conf; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXUdpRangeEtypeControl)) ) + return -EFAULT; + + switch (conf.udp_range_num) + { + case 0: + { + conf.port_start = (UDP_RANGE0_REG & 0xffff); + conf.port_end = ((UDP_RANGE0_REG >> 16 )& 0xffff); + break; + } + case 1: + { + conf.port_start = (UDP_RANGE1_REG & 0xffff); + conf.port_end = ((UDP_RANGE1_REG >> 16 )& 0xffff); + break; + } + case 2: + { + conf.port_start = (UDP_RANGE2_REG & 0xffff); + conf.port_end = ((UDP_RANGE2_REG >> 16 )& 0xffff); + break; + } + case 3: + { + conf.port_start = (UDP_RANGE3_REG & 0xffff); + conf.port_end = ((UDP_RANGE3_REG >> 16 )& 0xffff); + break; + } + } + + if (copy_to_user(ifr->ifr_data, &conf, sizeof(CNS3XXXEtypeControl)) ) + return -EFAULT; + + return CAVM_OK; +} + +int get_etype(struct ifreq *ifr) +{ + CNS3XXXEtypeControl conf; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXEtypeControl)) ) + return -EFAULT; + switch (conf.etype_num) + { + case 0: + { + conf.val = (ETYPE1_ETYPE0_REG & 0xffff); + conf.pri = (PRIO_ETYPE_UDP_REG & 0x7); + break; + } + case 1: + { + conf.val = ((ETYPE1_ETYPE0_REG >> 16 )& 0xffff); + conf.pri = ((PRIO_ETYPE_UDP_REG >> 4) & 0x7); + break; + } + case 2: + { + conf.val = (ETYPE3_ETYPE2_REG & 0xffff); + conf.pri = ((PRIO_ETYPE_UDP_REG >> 8) & 0x7); + break; + } + case 3: + { + conf.val = ((ETYPE3_ETYPE2_REG >> 16 )& 0xffff); + conf.pri = ((PRIO_ETYPE_UDP_REG >> 12) & 0x7); + break; + } + } + if (copy_to_user(ifr->ifr_data, &conf, sizeof(CNS3XXXEtypeControl)) ) + return -EFAULT; + + return CAVM_OK; +} + +int set_etype(struct ifreq *ifr) +{ + CNS3XXXEtypeControl conf; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXEtypeControl)) ) + return -EFAULT; + switch (conf.etype_num) + { + case 0: + { + ETYPE1_ETYPE0_REG &= (~0xffff); + ETYPE1_ETYPE0_REG |= conf.val; + + PRIO_ETYPE_UDP_REG &= (~7); + PRIO_ETYPE_UDP_REG |= (conf.pri); + break; + } + case 1: + { + ETYPE1_ETYPE0_REG &= (~(0xffff << 16)); + ETYPE1_ETYPE0_REG |= (conf.val << 16); + + PRIO_ETYPE_UDP_REG &= (~(7 << 4)); + PRIO_ETYPE_UDP_REG |= (conf.pri << 4); + break; + } + case 2: + { + ETYPE3_ETYPE2_REG &= (~0xffff); + ETYPE3_ETYPE2_REG |= conf.val; + + PRIO_ETYPE_UDP_REG &= (~(7 << 8)); + PRIO_ETYPE_UDP_REG |= (conf.pri << 8); + break; + } + case 3: + { + ETYPE3_ETYPE2_REG &= (~(0xffff << 16)); + ETYPE3_ETYPE2_REG |= (conf.val << 16); + + PRIO_ETYPE_UDP_REG &= (~(7 << 12)); + PRIO_ETYPE_UDP_REG |= (conf.pri << 12); + break; + } + } + return CAVM_OK; +} + +int get_pri_ip_dscp(struct ifreq *ifr) +{ + CNS3XXXPriIpDscpControl conf; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXPriIpDscpControl)) ) + return -EFAULT; + + if ( 0 <= conf.ip_dscp_num && conf.ip_dscp_num <= 7) { + conf.pri = ((PRIO_IPDSCP_7_0_REG >> (conf.ip_dscp_num * 4)) & 0x7); + } else if ( 8 <= conf.ip_dscp_num && conf.ip_dscp_num <= 15) { + conf.pri = ((PRIO_IPDSCP_15_8_REG >> ((conf.ip_dscp_num-8) * 4)) & 0x7); + } else if ( 16 <= conf.ip_dscp_num && conf.ip_dscp_num <= 23) { + conf.pri = ((PRIO_IPDSCP_23_16_REG >> ((conf.ip_dscp_num-16) * 4)) & 0x7); + } else if ( 24 <= conf.ip_dscp_num && conf.ip_dscp_num <= 31) { + conf.pri = ((PRIO_IPDSCP_31_24_REG >> ((conf.ip_dscp_num-24) * 4)) & 0x7); + } else if ( 32 <= conf.ip_dscp_num && conf.ip_dscp_num <= 39) { + conf.pri = ((PRIO_IPDSCP_39_32_REG >> ((conf.ip_dscp_num-32) * 4)) & 0x7); + } else if ( 40 <= conf.ip_dscp_num && conf.ip_dscp_num <= 47) { + conf.pri = ((PRIO_IPDSCP_47_40_REG >> ((conf.ip_dscp_num-40) * 4)) & 0x7); + } else if ( 48 <= conf.ip_dscp_num && conf.ip_dscp_num <= 55) { + conf.pri = ((PRIO_IPDSCP_55_48_REG >> ((conf.ip_dscp_num-48) * 4)) & 0x7); + } else if ( 56 <= conf.ip_dscp_num && conf.ip_dscp_num <= 63) { + conf.pri = ((PRIO_IPDSCP_63_56_REG >> ((conf.ip_dscp_num-56) * 4)) & 0x7); + } else { + return CAVM_ERR; + } + + + if (copy_to_user(ifr->ifr_data, &conf, sizeof(CNS3XXXPriIpDscpControl)) ) + return -EFAULT; + return CAVM_OK; +} + + +int set_pri_ip_dscp(struct ifreq *ifr) +{ + CNS3XXXPriIpDscpControl conf; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXPriIpDscpControl)) ) + return -EFAULT; + + if ( 0 <= conf.ip_dscp_num && conf.ip_dscp_num <= 7) { + PRIO_IPDSCP_7_0_REG &= (~(0x7 << (conf.ip_dscp_num * 4) ) ); + PRIO_IPDSCP_7_0_REG |= (conf.pri << (conf.ip_dscp_num * 4)); + } else if ( 8 <= conf.ip_dscp_num && conf.ip_dscp_num <= 15) { + PRIO_IPDSCP_15_8_REG &= (~(0x7 << ((conf.ip_dscp_num-8) * 4) ) ); + PRIO_IPDSCP_15_8_REG |= (conf.pri << ((conf.ip_dscp_num-8) * 4)); + } else if ( 16 <= conf.ip_dscp_num && conf.ip_dscp_num <= 23) { + PRIO_IPDSCP_23_16_REG &= (~(0x7 << ((conf.ip_dscp_num-16) * 4) ) ); + PRIO_IPDSCP_23_16_REG |= (conf.pri << ((conf.ip_dscp_num-16) * 4)); + + } else if ( 24 <= conf.ip_dscp_num && conf.ip_dscp_num <= 31) { + PRIO_IPDSCP_31_24_REG &= (~(0x7 << ((conf.ip_dscp_num-24) * 4) ) ); + PRIO_IPDSCP_31_24_REG |= (conf.pri << ((conf.ip_dscp_num-24) * 4)); + + } else if ( 32 <= conf.ip_dscp_num && conf.ip_dscp_num <= 39) { + PRIO_IPDSCP_39_32_REG &= (~(0x7 << ((conf.ip_dscp_num-32) * 4) ) ); + PRIO_IPDSCP_39_32_REG |= (conf.pri << ((conf.ip_dscp_num-32) * 4)); + + } else if ( 40 <= conf.ip_dscp_num && conf.ip_dscp_num <= 47) { + PRIO_IPDSCP_47_40_REG &= (~(0x7 << ((conf.ip_dscp_num-40) * 4) ) ); + PRIO_IPDSCP_47_40_REG |= (conf.pri << ((conf.ip_dscp_num-40) * 4)); + } else if ( 48 <= conf.ip_dscp_num && conf.ip_dscp_num <= 55) { + PRIO_IPDSCP_55_48_REG &= (~(0x7 << ((conf.ip_dscp_num-48) * 4) ) ); + PRIO_IPDSCP_55_48_REG |= (conf.pri << ((conf.ip_dscp_num-48) * 4)); + } else if ( 56 <= conf.ip_dscp_num && conf.ip_dscp_num <= 63) { + PRIO_IPDSCP_63_56_REG &= (~(0x7 << ((conf.ip_dscp_num-56) * 4) ) ); + PRIO_IPDSCP_63_56_REG |= (conf.pri << ((conf.ip_dscp_num-56) * 4)); + } else { + return CAVM_ERR; + } + return CAVM_OK; +} + + +int bcm53115M_reg_read_ioctl(struct ifreq *ifr) +{ + int bcm53115M_reg_read(int page, int offset, u8 *buf, int len); + CNS3XXXBCM53115M conf; + int __init_or_module gpio_direction_output(unsigned int pin, unsigned int state); + + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXBCM53115M)) ) + return -EFAULT; + printk("conf.page: %x\n", conf.page); + printk("conf.offset: %x\n", conf.offset); + printk("conf.data_len: %x\n", conf.data_len); + switch (conf.data_len) + { + case 1: + { + bcm53115M_reg_read(conf.page, conf.offset, (u8 *)&conf.u8_val, 1); + printk("conf.u8_val: %x\n", conf.u8_val); + break; + } + case 2: + { + bcm53115M_reg_read(conf.page, conf.offset, (u8 *)&conf.u16_val, 2); + printk("conf.u16_val: %x\n", conf.u16_val); + break; + } + case 4: + { + bcm53115M_reg_read(conf.page, conf.offset, (u8 *)&conf.u32_val, 4); + printk("conf.u32_val: %x\n", conf.u32_val); + break; + } + default: + { + printk("[kernel mode]: don't support date length: %d\n", conf.data_len); + } + } + + + + if (copy_to_user(ifr->ifr_data, &conf, sizeof(CNS3XXXBCM53115M)) ) + return -EFAULT; + return CAVM_OK; +} + +int bcm53115M_reg_write_ioctl(struct ifreq *ifr) +{ + int bcm53115M_reg_write(int page, int offset, u8 *buf, int len); + CNS3XXXBCM53115M conf; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXBCM53115M)) ) + return -EFAULT; + + switch (conf.data_len) + { + case 1: + { + bcm53115M_reg_write(conf.page, conf.offset, (u8 *)&conf.u8_val, 1); + break; + } + case 2: + { + bcm53115M_reg_write(conf.page, conf.offset, (u8 *)&conf.u16_val, 2); + break; + } + case 4: + { + bcm53115M_reg_write(conf.page, conf.offset, (u8 *)&conf.u32_val, 4); + break; + } + default: + { + printk("[kernel mode]: don't support date length: %d\n", conf.data_len); + } + } + return CAVM_OK; +} + +#if 0 +int get_rxring(struct ifreq *ifr) +{ + CNS3XXXRingStatus conf; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXRingStatus)) ) + return -EFAULT; + conf.rx_ring=g_ring_info.rx_ring; + conf.tx_ring=0; + if (copy_to_user(ifr->ifr_data, &conf, sizeof(CNS3XXXRingStatus)) ) + return -EFAULT; +} +#endif + +int dump_mib_counter(struct ifreq *ifr) +{ + CNS3XXXMIBCounter conf; + int addr=0,i=0; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(CNS3XXXMIBCounter)) ) + return -EFAULT; + + for (addr=0x300; addr <= 0x334 ; addr+=4) + conf.mib[i++]=SWITCH_REG_VALUE(addr); + for (addr=0x400; addr <= 0x434 ; addr+=4) + conf.mib[i++]=SWITCH_REG_VALUE(addr); + for (addr=0x600; addr <= 0x634 ; addr+=4) + conf.mib[i++]=SWITCH_REG_VALUE(addr); + // cpu mib counter + for (addr=0x500; addr <= 0x528 ; addr+=4) + conf.mib[i++]=SWITCH_REG_VALUE(addr); + conf.mib_len=i; + if (copy_to_user(ifr->ifr_data, &conf, sizeof(CNS3XXXMIBCounter)) ) + return -EFAULT; + return 0; +} + +// reference e100.c +int cns3xxx_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + CNS3XXXIoctlCmd ioctl_cmd; + + //printk("cns3xxx_do_ioctl begin\n"); + + if (cmd != SIOCDEVPRIVATE) { + return -EOPNOTSUPP; + } + if (copy_from_user(&ioctl_cmd, ifr->ifr_data, sizeof(CNS3XXXIoctlCmd))) + return -EFAULT; + + //printk("ioctl_cmd: %d\n", ioctl_cmd); + switch (ioctl_cmd) { + case CNS3XXX_ARP_REQUEST_SET: + { + CNS3XXXArpRequestControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXArpRequestControl)) ) + return -EFAULT; + + (ctl.val==0) ? (MAC_GLOB_CFG_REG &= (~(1 << 23)) ): (MAC_GLOB_CFG_REG |= (1 << 23) ); + + } + + case CNS3XXX_ARP_REQUEST_GET: + { + CNS3XXXArpRequestControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXArpRequestControl)) ) + return -EFAULT; + + ctl.val = ((MAC_GLOB_CFG_REG >> 23) & 1); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXArpRequestControl)) ) + return -EFAULT; + return CAVM_OK; + } + + case CNS3XXX_HOL_PREVENT_SET: + { + CNS3XXXHOLPreventControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXHOLPreventControl)) ) + return -EFAULT; + (ctl.enable == 1) ? (TC_CTRL_REG |= (1 << 29)) : (TC_CTRL_REG &= (~(1 << 29))) ; + + return CAVM_OK; + } + case CNS3XXX_HOL_PREVENT_GET: + { + CNS3XXXHOLPreventControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXHOLPreventControl)) ) + return -EFAULT; + + ctl.enable = ((TC_CTRL_REG >> 29) & 0x1); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXHOLPreventControl)) ) + return -EFAULT; + return CAVM_OK; + } + + // for S component or C conponent + case CNS3XXX_BRIDGE_SET: + { + CNS3XXXBridgeControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXBridgeControl)) ) + return -EFAULT; + (ctl.type == 1) ? (VLAN_CFG |= (1 << 1)) : (VLAN_CFG &= (~(1 << 1))) ; + + + } + case CNS3XXX_BRIDGE_GET: + { + CNS3XXXBridgeControl ctl; + + ctl.type = ((VLAN_CFG >> 1) & 0x1); + printk("[kernel mode] ctl.type: %d\n", ctl.type); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXBridgeControl)) ) + return -EFAULT; + + return CAVM_OK; + } + + case CNS3XXX_PORT_NEIGHBOR_SET: + { + CNS3XXXPortNeighborControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXPortNeighborControl)) ) + return -EFAULT; + switch (ctl.which_port) + { + case 0: + { + (ctl.type == 1) ? (VLAN_CFG |= (1 << 4)) : (VLAN_CFG &= (~(1 << 4))) ; + return 0; + } + case 1: + { + (ctl.type == 1) ? (VLAN_CFG |= (1 << 5)) : (VLAN_CFG &= (~(1 << 5))) ; + return 0; + } + case 2: + { + (ctl.type == 1) ? (VLAN_CFG |= (1 << 7)) : (VLAN_CFG &= (~(1 << 7))) ; + return 0; + } + case 3: // cpu port + { + (ctl.type == 1) ? (VLAN_CFG |= (1 << 6)) : (VLAN_CFG &= (~(1 << 6))) ; + return 0; + } + default: + return -EFAULT; + } + + } + + case CNS3XXX_PORT_NEIGHBOR_GET: + { + CNS3XXXPortNeighborControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXPortNeighborControl)) ) + return -EFAULT; + switch (ctl.which_port) + { + case 0: + { + ctl.type = ((VLAN_CFG >> 4 ) & 0x1); + break; + } + case 1: + { + ctl.type = ((VLAN_CFG >> 5 ) & 0x1); + break; + } + case 2: + { + ctl.type = ((VLAN_CFG >> 7 ) & 0x1); + break; + } + case 3: // cpu port + { + ctl.type = ((VLAN_CFG >> 6 ) & 0x1); + break; + } + } + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXPortNeighborControl)) ) + return -EFAULT; + + return CAVM_OK; + } + + case CNS3XXX_VLAN_TABLE_LOOKUP: + { + CNS3XXXVLANTableEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXVLANTableEntry)) ) + return -EFAULT; + if (cns3xxx_vlan_table_lookup(&ctl.entry) == CAVM_NOT_FOUND) { + return CAVM_NOT_FOUND; + } + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXVLANTableEntry))) + return -EFAULT; + + return CAVM_FOUND; + } + case CNS3XXX_VLAN_TABLE_READ: + { + CNS3XXXVLANTableEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXVLANTableEntry)) ) + { + return -EFAULT; + } + cns3xxx_vlan_table_read(&ctl.entry); + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXVLANTableEntry))) + return -EFAULT; + + return 0; + } + case CNS3XXX_VLAN_TABLE_ADD: + { + CNS3XXXVLANTableEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXVLANTableEntry)) ) + return -EFAULT; + cns3xxx_vlan_table_add(&ctl.entry); + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXVLANTableEntry))) + return -EFAULT; + + return 0; + } + + case CNS3XXX_ARL_TABLE_ADD: + { + CNS3XXXARLTableEntry ctl; + + printk("[kernel mode] CNS3XXX_ARL_TABLE_ADD\n"); + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXARLTableEntry)) ) + return -EFAULT; + cns3xxx_arl_table_add(&ctl.entry); + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXARLTableEntry))) + return -EFAULT; + + return 0; + } + + + case CNS3XXX_ARL_TABLE_DEL: + { + CNS3XXXARLTableEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXARLTableEntry)) ) + return -EFAULT; + cns3xxx_arl_table_invalid(&ctl.entry); + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXARLTableEntry))) + return -EFAULT; + + return 0; + } + case CNS3XXX_VLAN_TABLE_DEL: + { + CNS3XXXARLTableEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXARLTableEntry)) ) + return -EFAULT; + cns3xxx_arl_table_invalid(&ctl.entry); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXARLTableEntry))) + return -EFAULT; + + return CAVM_FOUND; + } + + case CNS3XXX_ARL_TABLE_SEARCH: + { + CNS3XXXARLTableEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXARLTableEntry)) ) + return -EFAULT; + if (cns3xxx_arl_table_search(&ctl.entry) == CAVM_NOT_FOUND){ + printk("[kernel mode] not found\n"); + return CAVM_NOT_FOUND; + } + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXARLTableEntry))) + return -EFAULT; + + return CAVM_FOUND; + } + case CNS3XXX_ARL_IS_TABLE_END: + { + CNS3XXXARLTableEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXARLTableEntry)) ) + return -EFAULT; + if (cns3xxx_is_arl_table_end() == CAVM_ERR) + return CAVM_ERR; + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXARLTableEntry))) + return -EFAULT; + + return CAVM_OK; + } + + case CNS3XXX_ARL_TABLE_SEARCH_AGAIN: + { + CNS3XXXARLTableEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXARLTableEntry)) ) + return -EFAULT; + if (cns3xxx_arl_table_search_again(&ctl.entry) == CAVM_NOT_FOUND) + return CAVM_NOT_FOUND; + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXARLTableEntry))) + return -EFAULT; + + return CAVM_FOUND; + } + + case CNS3XXX_ARL_TABLE_FLUSH: + { + CNS3XXXARLTableEntry ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXARLTableEntry)) ) + return -EFAULT; + + cns3xxx_arl_table_flush(); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXARLTableEntry))) + return -EFAULT; + + return CAVM_FOUND; + } + + + + case CNS3XXX_ARL_TABLE_LOOKUP: + { + CNS3XXXARLTableEntry ctl; + + + printk("[kernel mode] in CNS3XXX_ARL_TABLE_LOOKUP\n"); + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXARLTableEntry)) ) + return -EFAULT; + if (cns3xxx_arl_table_lookup(&ctl.entry) == CAVM_NOT_FOUND) + return CAVM_NOT_FOUND; + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXARLTableEntry))) + return -EFAULT; + + return CAVM_FOUND; + } + + case CNS3XXX_TC_SET: + { + CNS3XXXTrafficClassControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXTrafficClassControl)) ) + return -EFAULT; + TC_CTRL_REG &= (~(0x3 << 30)); + TC_CTRL_REG |= (ctl.tc << 30); + return CAVM_OK; + } + case CNS3XXX_TC_GET: + { + CNS3XXXTrafficClassControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXTrafficClassControl)) ) + return -EFAULT; + + ctl.tc = ((TC_CTRL_REG >> 30) & 0x3); + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXTrafficClassControl)) ) + return -EFAULT; + + return CAVM_OK; + } + + case CNS3XXX_PRI_CTRL_SET: + { + CNS3XXXPriCtrlControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXPriCtrlControl)) ) + return -EFAULT; + + switch (ctl.which_port) + { + case 0: + { + MAC0_PRI_CTRL_REG &= (~(0x7 << 24)); + MAC0_PRI_CTRL_REG &= (~(0xf << 18)); + + MAC0_PRI_CTRL_REG |= (ctl.port_pri << 24); + + MAC0_PRI_CTRL_REG |= (ctl.ether_pri_en << 18); + MAC0_PRI_CTRL_REG |= (ctl.vlan_pri_en << 19); + MAC0_PRI_CTRL_REG |= (ctl.dscp_pri_en << 20); + MAC0_PRI_CTRL_REG |= (ctl.udp_pri_en << 21); + break; + } + case 1: + { + MAC1_PRI_CTRL_REG &= (~(0x7 << 24)); + MAC1_PRI_CTRL_REG &= (~(0xf << 18)); + + MAC1_PRI_CTRL_REG |= (ctl.port_pri << 24); + + MAC1_PRI_CTRL_REG |= (ctl.ether_pri_en << 18); + MAC1_PRI_CTRL_REG |= (ctl.vlan_pri_en << 19); + MAC1_PRI_CTRL_REG |= (ctl.dscp_pri_en << 20); + MAC1_PRI_CTRL_REG |= (ctl.udp_pri_en << 21); + break; + } + case 2: + { + MAC2_PRI_CTRL_REG &= (~(0x7 << 24)); + MAC2_PRI_CTRL_REG &= (~(0xf << 18)); + + MAC2_PRI_CTRL_REG |= (ctl.port_pri << 24); + + MAC2_PRI_CTRL_REG |= (ctl.ether_pri_en << 18); + MAC2_PRI_CTRL_REG |= (ctl.vlan_pri_en << 19); + MAC2_PRI_CTRL_REG |= (ctl.dscp_pri_en << 20); + MAC2_PRI_CTRL_REG |= (ctl.udp_pri_en << 21); + break; + } + case 3: // cpu + { + printk("[kernel mode] CPU_PRI_CTRL_REG: %#x\n", CPU_PRI_CTRL_REG); + CPU_PRI_CTRL_REG &= (~(0x7 << 24)); + CPU_PRI_CTRL_REG &= (~(0xf << 18)); + + CPU_PRI_CTRL_REG |= (ctl.port_pri << 24); + + CPU_PRI_CTRL_REG |= (ctl.ether_pri_en << 18); + CPU_PRI_CTRL_REG |= (ctl.vlan_pri_en << 19); + CPU_PRI_CTRL_REG |= (ctl.dscp_pri_en << 20); + CPU_PRI_CTRL_REG |= (ctl.udp_pri_en << 21); + break; + } + } + + return CAVM_OK; + } + + case CNS3XXX_PRI_CTRL_GET: + { + CNS3XXXPriCtrlControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXPriCtrlControl)) ) + return -EFAULT; + + + if (copy_to_user(ifr->ifr_data, &ctl, sizeof(CNS3XXXPriCtrlControl)) ) + return -EFAULT; + + return CAVM_OK; + } + + case CNS3XXX_DMA_RING_CTRL_SET: + { + CNS3XXXDmaRingCtrlControl ctl; + + if (copy_from_user(&ctl, ifr->ifr_data, sizeof(CNS3XXXDmaRingCtrlControl)) ) + return -EFAULT; + + (ctl.ts_double_ring_en == 0) ? DMA_RING_CTRL_REG &= (~(0x1 << 16)) : (DMA_RING_CTRL_REG |= (ctl.ts_double_ring_en << 16)); + (ctl.fs_double_ring_en == 0) ? DMA_RING_CTRL_REG &= (~(0x1 << 0)) : (DMA_RING_CTRL_REG |= (ctl.fs_double_ring_en << 0)); + (ctl.fs_pkt_allocate == 0) ? DMA_RING_CTRL_REG &= (~(0x1 << 1)) : (DMA_RING_CTRL_REG |= (ctl.fs_pkt_allocate << 1)); + } + + case CNS3XXX_PRI_IP_DSCP_SET: + { + return set_pri_ip_dscp(ifr); + } + case CNS3XXX_PRI_IP_DSCP_GET: + { + return get_pri_ip_dscp(ifr); + } + + case CNS3XXX_ETYPE_SET: + { + return set_etype(ifr); + } + case CNS3XXX_ETYPE_GET: + { + return get_etype(ifr); + } + + case CNS3XXX_UDP_RANGE_SET: + { + return set_udp_range(ifr); + } + case CNS3XXX_UDP_RANGE_GET: + { + return get_udp_range(ifr); + } + + case CNS3XXX_RATE_LIMIT_SET: + { + return set_rate_limit(ifr); + } + case CNS3XXX_RATE_LIMIT_GET: + { + return get_rate_limit(ifr); + } + case CNS3XXX_QUEUE_WEIGHT_SET: + { + return set_queue_weight(ifr); + } + case CNS3XXX_QUEUE_WEIGHT_GET: + { + return get_queue_weight(ifr); + } + + case CNS3XXX_FC_RLS_SET: + { + return set_fc_rls(ifr); + } + case CNS3XXX_FC_RLS_GET: + { + return get_fc_rls(ifr); + } + + case CNS3XXX_FC_SET_SET: + { + return set_fc_set(ifr); + } + case CNS3XXX_FC_SET_GET: + { + return get_fc_set(ifr); + } + + case CNS3XXX_SARL_RLS_SET: + { + return set_sarl_rls(ifr); + } + case CNS3XXX_SARL_RLS_GET: + { + return get_sarl_rls(ifr); + } + + case CNS3XXX_SARL_SET_SET: + { + return set_sarl_set(ifr); + } + case CNS3XXX_SARL_SET_GET: + { + return get_sarl_set(ifr); + } + + case CNS3XXX_SARL_OQ_SET: + { + return set_sarl_oq(ifr); + } + case CNS3XXX_SARL_OQ_GET: + { + return get_sarl_oq(ifr); + } + + case CNS3XXX_SARL_ENABLE_SET: + { + return set_sarl_enable(ifr); + } + case CNS3XXX_SARL_ENABLE_GET: + { + return get_sarl_enable(ifr); + } + + case CNS3XXX_FC_SET: + { + return set_fc(ifr); + } + case CNS3XXX_FC_GET: + { + return get_fc(ifr); + } + + case CNS3XXX_IVL_SET: + { + return set_ivl(ifr); + } + case CNS3XXX_IVL_GET: + { + return get_ivl(ifr); + } + + case CNS3XXX_WAN_PORT_SET: + { + return set_wan_port(ifr); + } + case CNS3XXX_WAN_PORT_GET: + { + return get_wan_port(ifr); + } + + case CNS3XXX_PVID_SET: + { + return set_pvid(ifr); + } + case CNS3XXX_PVID_GET: + { + return get_pvid(ifr); + } + + case CNS3XXX_QA_GET: + { + return get_qa(ifr); + } + case CNS3XXX_QA_SET: + { + return set_qa(ifr); + } + + case CNS3XXX_PACKET_MAX_LEN_GET: + { + return get_packet_max_len(ifr); + } + case CNS3XXX_PACKET_MAX_LEN_SET: + { + return set_packet_max_len(ifr); + } + + case CNS3XXX_BCM53115M_REG_READ: + { + return bcm53115M_reg_read_ioctl(ifr); + } + case CNS3XXX_BCM53115M_REG_WRITE: + { + return bcm53115M_reg_write_ioctl(ifr); + } + +#if 0 + case CNS3XXX_RXRING_STATUS: + { + return get_rxring(ifr); + } +#endif + case CNS3XXX_DUMP_MIB_COUNTER: + { + return dump_mib_counter(ifr); + } + + + default: + { + printk("[kernel mode] don't match any command\n"); + break; + } + + } // end switch (ioctl_cmd) + return 0; +} + +#ifdef CONFIG_CNS3XXX_NAPI +static int cns3xxx_poll(struct napi_struct *napi, int budget) +{ + + CNS3XXXPrivate *sp = container_of(napi, CNS3XXXPrivate, napi); + int work_done = 0; + int work_to_do = budget; // define minima value + + cns3xxx_receive_packet(sp, 0, &work_done, work_to_do); + + budget -= work_done; + + if (work_done) { + if (test_bit(0, (unsigned long *)&sp->is_qf) == 1){ + clear_bit(0, (unsigned long *)&sp->is_qf); + enable_rx_dma(sp->ring_index, 1); + return 1; + } + } else { + //netif_rx_complete(napi_dev, &sp->napi); + napi_complete(napi); +#ifdef CNS3XXX_USE_MASK + cns3xxx_write_pri_mask(0xf0); +#else + if (sp->ring_index == 0) + cns3xxx_enable_irq(FSRC_RING0_INTERRUPT_ID); + else + cns3xxx_enable_irq(FSRC_RING1_INTERRUPT_ID); +#endif + return 0; + } + + return 1; +} +#endif + +static struct net_device_stats *cns3xxx_get_stats(struct net_device *dev) +{ + CNS3XXXPrivate *priv = netdev_priv(dev); + + return &priv->stats; +} + +static int cns3xxx_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < cns3xxx_min_mtu() || new_mtu > cns3xxx_max_mtu()) + return -EINVAL; + + dev->mtu = new_mtu; + + return 0; +} + +static void cns3xxx_timeout(struct net_device *dev) +{ + //star_gsw_enable(dev); + netif_wake_queue(dev); + dev->trans_start = jiffies; +} + +#ifdef LINUX2631 +static const struct net_device_ops cns3xxx_netdev_ops = { + .ndo_open = cns3xxx_open, + .ndo_stop = cns3xxx_close, + .ndo_start_xmit = cns3xxx_send_packet, + //.ndo_validate_addr = eth_validate_addr, + //.ndo_set_multicast_list = cns3xxx_set_multicast_list, + .ndo_set_mac_address = cns3xxx_set_mac_addr, + .ndo_change_mtu = cns3xxx_change_mtu, + .ndo_do_ioctl = cns3xxx_do_ioctl, + .ndo_tx_timeout = cns3xxx_timeout, + .ndo_get_stats = cns3xxx_get_stats, + +#if defined(CNS3XXX_VLAN_8021Q) + .ndo_vlan_rx_register = cns3xxx_vlan_rx_register, + //.ndo_vlan_rx_add_vid = e1000_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = cns3xxx_vlan_rx_kill_vid, +#endif + +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cns3xxx_netpoll, +#endif +}; +#endif // LINUX2631 + +static int __init cns3xxx_probe(RingInfo ring_info) +{ + void cns3xxx_set_ethtool_ops(struct net_device *netdev); + + int netdev_size = sizeof(net_device_prive)/sizeof(NetDevicePriv); + int i=0, err=0; + struct net_device *netdev=0; + CNS3XXXPrivate *priv=0; + struct sockaddr sock_addr; + + for (i=0 ; i < netdev_size ; ++i) { + if (init_port & (1 << i)) { + + netdev = alloc_etherdev(sizeof(CNS3XXXPrivate)); + if (!netdev) { + err = -ENOMEM; + goto err_alloc_etherdev; + } + if (net_device_prive[i].name) + strcpy(netdev->name, net_device_prive[i].name); + + + net_dev_array[net_device_prive[i].vlan_tag] = netdev; + if (intr_netdev==0) + intr_netdev = netdev; + + SET_NETDEV_DEV(netdev, NULL); + priv = netdev_priv(netdev); + spin_lock_init(&priv->lock); + memset(priv, 0, sizeof(CNS3XXXPrivate)); + +#if 1 + priv->num_rx_queues = ring_info.num_rx_queues; + priv->num_tx_queues = ring_info.num_tx_queues; + priv->rx_ring = ring_info.rx_ring; + priv->tx_ring = ring_info.tx_ring; +#endif + + priv->net_device_priv = &net_device_prive[i]; + + // set netdev MAC address + memcpy(sock_addr.sa_data, net_device_prive[i].mac, 6); + cns3xxx_set_mac_addr(netdev, &sock_addr); + +#ifdef LINUX2631 + netdev->netdev_ops = &cns3xxx_netdev_ops; +#endif + + cns3xxx_set_ethtool_ops(netdev); +#ifdef LINUX2627 + //netdev->base_addr = IO_ADDRESS(GSW_BASE_ADDR); + netdev->base_addr = 0; + netdev->open = cns3xxx_open; + netdev->stop = cns3xxx_close; + netdev->hard_start_xmit = cns3xxx_send_packet; + //netdev->hard_start_xmit = 0; + netdev->do_ioctl = cns3xxx_do_ioctl; + netdev->change_mtu = cns3xxx_change_mtu; + + //netdev->get_stats = cns3xxx_get_stats; + netdev->watchdog_timeo = 5 * HZ; // ref e1000_main.c + netdev->tx_timeout = cns3xxx_timeout; + netdev->set_mac_address = cns3xxx_set_mac_addr; +#endif + +#if defined(CNS3XXX_TX_HW_CHECKSUM) + netdev->features |= (NETIF_F_IP_CSUM | NETIF_F_SG); + //netdev->features |= (NETIF_F_HW_CSUM | NETIF_F_SG); +#endif + + +#ifdef CONFIG_CNS3XXX_NAPI + //netif_napi_add(netdev, &priv->napi, cns3xxx_poll, CNS3XXX_NAPI_WEIGHT); +#endif + +#if defined(CNS3XXX_VLAN_8021Q) + // do not let 8021Q module insert vlan tag + // can use the snippet code to get vlan tage + // if (priv->vlgrp && vlan_tx_tag_present(skb)) + // vlan_tag = cpu_to_be16(vlan_tx_tag_get(skb)); +#ifdef CNS3XXX_8021Q_HW_TX + // hardware support insert VLAN tag on TX path + netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; +#else + netdev->features |= NETIF_F_HW_VLAN_RX; // remove NETIF_F_HW_VLAN_TX flag that 8021Q module to insert vlan tag. +#endif + + //netdev->vlan_rx_register = cns3xxx_vlan_rx_register; + //netdev->vlan_rx_kill_vid = cns3xxx_vlan_rx_kill_vid; +#endif + + + err = register_netdev(netdev); + if (err) { + goto err_register_netdev; + } + + netif_carrier_off(netdev); + netdev = 0; + } + } // for (i=0 ; i < netdev_size ; ++i) + + return 0; + + +err_register_netdev: + free_netdev(netdev); + +err_alloc_etherdev: + return err; +} + +int cns3xxx_gsw_config_mac_port0(void) +{ + INIT_PORT0_PHY + INIT_PORT0_MAC + PORT0_LINK_DOWN + return 0; +} + +int cns3xxx_gsw_config_mac_port1(void) +{ + INIT_PORT1_PHY + INIT_PORT1_MAC + PORT1_LINK_DOWN + return 0; +} + +int cns3xxx_gsw_config_mac_port2(void) +{ + INIT_PORT2_PHY + INIT_PORT2_MAC + PORT2_LINK_DOWN + return 0; +} + +static int cns3xxx_notify_reboot(struct notifier_block *nb, unsigned long event, void *ptr) +{ + // stop the DMA + enable_rx_dma(0, 0); + enable_tx_dma(0, 0); + enable_rx_dma(1, 0); + enable_tx_dma(1, 0); + + // disable Port 0 + enable_port(0, 0); + enable_port(1, 0); + enable_port(2, 0); + enable_port(3, 0); + return NOTIFY_DONE; +} + +#ifdef CONFIG_CNS3XXX_NAPI +static struct net_device *init_napi_dev(struct net_device *ndev, const RingInfo *ring_info) +{ + CNS3XXXPrivate *priv; + + ndev = alloc_etherdev(sizeof(CNS3XXXPrivate)); + if (!ndev) { + BUG(); + } + priv = netdev_priv(ndev); + memset(priv, 0, sizeof(CNS3XXXPrivate)); + + //priv = netdev_priv(napi_dev); + priv->num_rx_queues = ring_info->num_rx_queues; + priv->num_tx_queues = ring_info->num_tx_queues; + priv->rx_ring = ring_info->rx_ring; + priv->tx_ring = ring_info->tx_ring; + //priv->is_qf=0; // because of memset, so need not the line + + netif_napi_add(ndev, &priv->napi , cns3xxx_poll, CNS3XXX_NAPI_WEIGHT); + dev_hold(ndev); + set_bit(__LINK_STATE_START, &ndev->state); + + return ndev; +} +#endif + + +void cns3xxx_config_intr(void) +{ + u32 v=0xffffffff; + + get_interrupt_type(FSRC_RING0_INTERRUPT_ID, &v); +#if 1 + set_interrupt_type(FSRC_RING0_INTERRUPT_ID, RISING_EDGE); + get_interrupt_type(FSRC_RING0_INTERRUPT_ID, &v); + + get_interrupt_type(FSRC_RING1_INTERRUPT_ID, &v); + set_interrupt_type(FSRC_RING1_INTERRUPT_ID, RISING_EDGE); + get_interrupt_type(FSRC_RING1_INTERRUPT_ID, &v); + + get_interrupt_type(FSQF_RING0_INTERRUPT_ID, &v); + set_interrupt_type(FSQF_RING0_INTERRUPT_ID, RISING_EDGE); + get_interrupt_type(FSQF_RING0_INTERRUPT_ID, &v); + + get_interrupt_type(FSQF_RING1_INTERRUPT_ID, &v); + set_interrupt_type(FSQF_RING1_INTERRUPT_ID, RISING_EDGE); + get_interrupt_type(FSQF_RING1_INTERRUPT_ID, &v); + + #ifdef CNS3XXX_USE_MASK + get_interrupt_pri(FSRC_RING0_INTERRUPT_ID, &v); + set_interrupt_pri(FSRC_RING0_INTERRUPT_ID, 0xc); + get_interrupt_pri(FSRC_RING0_INTERRUPT_ID, &v); + + get_interrupt_pri(FSRC_RING1_INTERRUPT_ID, &v); + set_interrupt_pri(FSRC_RING1_INTERRUPT_ID, 0xc); + get_interrupt_pri(FSRC_RING1_INTERRUPT_ID, &v); + + get_interrupt_pri(FSQF_RING1_INTERRUPT_ID, &v); + set_interrupt_pri(FSQF_RING1_INTERRUPT_ID, 0xc); + get_interrupt_pri(FSQF_RING1_INTERRUPT_ID, &v); + + #ifndef CONFIG_CNS3XXX_NAPI + set_interrupt_pri(FSQF_RING0_INTERRUPT_ID, 0xc); + #endif + + + #endif // CNS3XXX_USE_MASK +#endif +} + +static int __devinit cns3xxx_init(struct platform_device *pdev) +{ + // when tx_ring/rx_ring alloc memory, + // don't free them until cns3xxx_exit_module + + struct eth_plat_info *plat = pdev->dev.platform_data; + init_port = plat->ports; + memcpy(cpu_vlan_table_entry.my_mac, plat->cpu_hwaddr, ETH_ALEN); +#if defined (CONFIG_CNS3XXX_SPPE) + memcpy(net_device_prive[3].mac, plat->cpu_hwaddr, ETH_ALEN); +#endif + + RingInfo ring_info; + int i=0; + //spin_lock_init(&star_gsw_send_lock); + + +#ifdef CNS3XXX_DOUBLE_RX_RING + ring_info.num_rx_queues = 2; +#else + ring_info.num_rx_queues = 1; +#endif + +#ifdef CNS3XXX_DOUBLE_TX_RING + ring_info.num_tx_queues = 2; +#else + ring_info.num_tx_queues = 1; +#endif + + ring_info.rx_ring = kcalloc(ring_info.num_rx_queues, sizeof(RXRing), GFP_KERNEL); + if (!ring_info.rx_ring) + return -ENOMEM; + + for (i=0 ; i < ring_info.num_rx_queues ; ++i) { + memset(ring_info.rx_ring + i, 0, sizeof(RXRing)); + } + + + ring_info.tx_ring = kcalloc(ring_info.num_tx_queues, sizeof(TXRing), GFP_KERNEL); + + + if (!ring_info.tx_ring) + return -ENOMEM; + + for (i=0 ; i < ring_info.num_tx_queues ; ++i) { + memset(ring_info.tx_ring + i, 0, sizeof(TXRing)); + } + + + g_ring_info = ring_info; + + cns3xxx_gsw_hw_init(); + +#ifdef CONFIG_FPGA + // GIGA mode disable + MAC0_CFG_REG &= (~(1<<16)); + MAC1_CFG_REG &= (~(1<<16)); + MAC2_CFG_REG &= (~(1<<16)); +#endif + + if ((init_port & 1) == 1) { + memcpy(vlan_table_entry[0].my_mac, plat->eth0_hwaddr, ETH_ALEN); + memcpy(arl_table_entry[0].mac, plat->eth0_hwaddr, ETH_ALEN); + memcpy(net_device_prive[0].mac, plat->eth0_hwaddr, ETH_ALEN); + cns3xxx_gsw_config_mac_port0(); + } + + if (((init_port >> 1) & 1) == 1) { + memcpy(vlan_table_entry[1].my_mac, plat->eth1_hwaddr, ETH_ALEN); + memcpy(arl_table_entry[1].mac, plat->eth1_hwaddr, ETH_ALEN); + memcpy(net_device_prive[1].mac, plat->eth1_hwaddr, ETH_ALEN); + cns3xxx_gsw_config_mac_port1(); + } + + if (((init_port >> 2) & 1) == 1) { + memcpy(vlan_table_entry[2].my_mac, plat->eth2_hwaddr, ETH_ALEN); + memcpy(arl_table_entry[2].mac, plat->eth2_hwaddr, ETH_ALEN); + memcpy(net_device_prive[2].mac, plat->eth2_hwaddr, ETH_ALEN); + cns3xxx_gsw_config_mac_port2(); + } + + cns3xxx_probe(ring_info); + cns3xxx_config_intr(); + +#ifdef CNS3XXX_VLAN_8021Q +#ifdef CNS3XXX_NIC_MODE_8021Q + cns3xxx_nic_mode(1); +#endif +#endif + spin_lock_init(&tx_lock); + spin_lock_init(&rx_lock); + +#ifdef CONFIG_CNS3XXX_NAPI + napi_dev = init_napi_dev(napi_dev, &ring_info); + #ifdef CNS3XXX_DOUBLE_RX_RING + r1_napi_dev = init_napi_dev(r1_napi_dev, &ring_info); + #endif +#endif + + register_reboot_notifier(&cns3xxx_notifier_reboot); + clear_fs_dma_state(0); + + if (ring_info.num_rx_queues == 2) { + // enable RX dobule ring + DMA_RING_CTRL_REG |= 1; + } + + if (ring_info.num_tx_queues == 2 ) { + // enable TX dobule ring + DMA_RING_CTRL_REG |= (1 << 16); + } + + + return 0; +} + +static int __devexit cns3xxx_remove(struct platform_device *pdev) +{ + int i=0; + +#if 1 + for (i=0 ; i < NETDEV_SIZE ; ++i) { + CNS3XXXPrivate *priv = 0; + + if (net_dev_array[i]){ + priv = netdev_priv(net_dev_array[i]); + + kfree(priv->tx_ring); + priv->tx_ring = 0; + + kfree(priv->rx_ring); + priv->rx_ring = 0; + + unregister_netdev(net_dev_array[i]); + free_netdev(net_dev_array[i]); + } + + +#if 0 + sprintf(netdev_name, "eth%d", i); + netdev=__dev_get_by_name(&init_net, netdev_name); + // if no unregister_netdev and free_netdev, + // after remove module, ifconfig will hang. + #if 1 + if (netdev) { + unregister_netdev(netdev); + free_netdev(netdev); + } +#endif + #endif + } +#endif + +#ifdef CONFIG_CNS3XXX_NAPI + free_netdev(napi_dev); + #ifdef CNS3XXX_DOUBLE_RX_RING + free_netdev(r1_napi_dev); + #endif +#endif + + +#if 0 + //star_gsw_buffer_free(); +#endif + unregister_reboot_notifier(&cns3xxx_notifier_reboot); +} + + +// this snippet code ref 8139cp.c +#if defined(CNS3XXX_VLAN_8021Q) +void cns3xxx_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ + CNS3XXXPrivate *priv = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + priv->vlgrp = grp; + spin_unlock_irqrestore(&priv->lock, flags); +} + +void cns3xxx_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +{ + CNS3XXXPrivate *priv = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + // reference: linux-2.6.24-current/drivers/netvia-velocity.c + vlan_group_set_device(priv->vlgrp, vid, NULL); + //priv->vlgrp->vlan_devices[vid] = NULL; + spin_unlock_irqrestore(&priv->lock, flags); +} + +#endif + +static struct platform_driver drv = { + .driver.name = "cns3xxx-net", + .probe = cns3xxx_init, + .remove = cns3xxx_remove, +}; + +static int __init cns3xxx_init_module(void) +{ + return platform_driver_register(&drv); +} + +static void __exit cns3xxx_exit_module(void) +{ + platform_driver_unregister(&drv); +} + +MODULE_AUTHOR("Cavium Networks, "); +MODULE_DESCRIPTION("CNS3XXX Switch Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(cns3xxx_init_module); +module_exit(cns3xxx_exit_module); + --- /dev/null +++ b/drivers/net/cns3xxx/cns3xxx_phy.c @@ -0,0 +1,1968 @@ +/******************************************************************************* + * + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT +1* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +#include "cns3xxx_phy.h" +#include "cns3xxx_symbol.h" + + +#if defined(LINUX_KERNEL) +#include "cns3xxx_tool.h" +#include // for CAVM_OK ... macro +#include +#include "cns3xxx_config.h" +#else // u-boot +#include +#include "cns3xxx_switch_type.h" +#define printk printf +#endif + +int cns3xxx_phy_reset(u8 phy_addr) +{ + u16 phy_data=0; + + if (cns3xxx_read_phy(phy_addr, 0, &phy_data) != CAVM_OK) + return CAVM_ERR; + phy_data |= (0x1 << 15); + if (cns3xxx_write_phy(phy_addr, 0, phy_data) != CAVM_OK) + return CAVM_ERR; + + return CAVM_OK; +} + +// mac_port: 0, 1, 2 +int cns3xxx_enable_mac_clock(u8 mac_port, u8 en) +{ + switch (mac_port) + { + case 0: + { + (en==1)?(PHY_AUTO_ADDR_REG |= 1 << 7) :(PHY_AUTO_ADDR_REG &= (~(1 << 7)) ); + break; + } + case 1: + { + (en==1)?(PHY_AUTO_ADDR_REG |= (1 << 15)) :(PHY_AUTO_ADDR_REG &= (~(1 << 15)) ); + break; + } + case 2: + { + (en==1)?(PHY_AUTO_ADDR_REG |= (1 << 23)) :(PHY_AUTO_ADDR_REG &= (~(1 << 23)) ); + break; + } + } + + return CAVM_OK; +} + +// dis: 1 disable +// dis: 0 enable +int cns3xxx_phy_auto_polling_enable(u8 port, u8 en) +{ + u8 phy_addr[]={5, 13, 21}; + + PHY_AUTO_ADDR_REG &= (~(1 << phy_addr[port])); + if (en) { + PHY_AUTO_ADDR_REG |= (1 << phy_addr[port]); + } + return CAVM_OK; +} + +// dis: 1 disable +// dis: 0 enable +int cns3xxx_mdc_mdio_disable(u8 dis) +{ + + PHY_CTRL_REG &= (~(1 << 7)); + if (dis) { + PHY_CTRL_REG |= (1 << 7); + } + return CAVM_OK; +} + + +static int cns3xxx_phy_auto_polling_conf(int mac_port, u8 phy_addr) +{ + if ( (mac_port < 0) || (mac_port > 2) ) { + return CAVM_ERR; + } + + switch (mac_port) + { + case 0: + { + PHY_AUTO_ADDR_REG &= (~0x1f); + PHY_AUTO_ADDR_REG |= phy_addr; + break; + } + case 1: + { + PHY_AUTO_ADDR_REG &= (~(0x1f << 8)); + PHY_AUTO_ADDR_REG |= (phy_addr << 8); + break; + } + case 2: + { + PHY_AUTO_ADDR_REG &= (~(0x1f << 16)); + PHY_AUTO_ADDR_REG |= (phy_addr << 16); + break; + } + } + cns3xxx_phy_auto_polling_enable(mac_port, 1); + return CAVM_OK; +} + + + +int cns3xxx_read_phy(u8 phy_addr, u8 phy_reg, u16 *read_data) +{ + int delay=0; + u32 volatile tmp = PHY_CTRL_REG; + + PHY_CTRL_REG |= (1 << 15); // clear "command completed" bit + // delay + for (delay=0; delay<10; delay++); + tmp &= (~0x1f); + tmp |= phy_addr; + + tmp &= (~(0x1f << 8)); + tmp |= (phy_reg << 8); + + tmp |= (1 << 14); // read command + + PHY_CTRL_REG = tmp; + + // wait command complete + while ( ((PHY_CTRL_REG >> 15) & 1) == 0); + + *read_data = (PHY_CTRL_REG >> 16); + + PHY_CTRL_REG |= (1 << 15); // clear "command completed" bit + + return CAVM_OK; +} + +int cns3xxx_write_phy(u8 phy_addr, u8 phy_reg, u16 write_data) +{ + int delay=0; + u32 tmp = PHY_CTRL_REG; + + PHY_CTRL_REG |= (1 << 15); // clear "command completed" bit + // delay + for (delay=0; delay<10; delay++); + + tmp &= (~(0xffff << 16)); + tmp |= (write_data << 16); + + tmp &= (~0x1f); + tmp |= phy_addr; + + tmp &= (~(0x1f << 8)); + tmp |= (phy_reg << 8); + + tmp |= (1 << 13); // write command + + PHY_CTRL_REG = tmp; + + // wait command complete + while ( ((PHY_CTRL_REG >> 15) & 1) == 0); + + return CAVM_OK; +} + +// port 0,1,2 +void cns3xxx_rxc_dly(u8 port, u8 val) +{ + switch (port) + { + case 0: + { + SLK_SKEW_CTRL_REG &= (~(0x3 << 4)); + SLK_SKEW_CTRL_REG |= (val << 4); + break; + } + case 1: + { + SLK_SKEW_CTRL_REG &= (~(0x3 << 12)); + SLK_SKEW_CTRL_REG |= (val << 12); + break; + } + case 2: + { + SLK_SKEW_CTRL_REG &= (~(0x3 << 20)); + SLK_SKEW_CTRL_REG |= (val << 20); + break; + } + } +} + +// port 0,1,2 +void cns3xxx_txc_dly(u8 port, u8 val) +{ + switch (port) + { + case 0: + { + SLK_SKEW_CTRL_REG &= (~(0x3 << 6)); + SLK_SKEW_CTRL_REG |= (val << 6); + break; + } + case 1: + { + SLK_SKEW_CTRL_REG &= (~(0x3 << 14)); + SLK_SKEW_CTRL_REG |= (val << 14); + break; + } + case 2: + { + SLK_SKEW_CTRL_REG &= (~(0x3 << 22)); + SLK_SKEW_CTRL_REG |= (val << 22); + break; + } + } +} + +void cns3xxx_mac2_gtxd_dly(u8 val) +{ + SLK_SKEW_CTRL_REG &= (~(0x3 << 24)); + SLK_SKEW_CTRL_REG |= (val << 24); +} + +// VITESSE suggest use isolate bit. +int vsc8601_power_down(int phy_addr, int y) +{ + u16 phy_data = 0; + /* set isolate bit instead of powerdown */ + cns3xxx_read_phy(phy_addr, 0, &phy_data); + if (y==1) // set isolate + phy_data |= (0x1 << 10); + if (y==0) // unset isolate + phy_data &= (~(0x1 << 10)); + cns3xxx_write_phy(phy_addr, 0, phy_data); + + return 0; +} + + +// port : 0 => port0 ; port : 1 => port1 +// y = 1 ; disable AN +void disable_AN(int port, int y) +{ + u32 mac_port_config=0; + + switch (port) + { + case 0: + { + mac_port_config = MAC0_CFG_REG; + break; + } + case 1: + { + mac_port_config = MAC1_CFG_REG; + break; + } + case 2: + { + mac_port_config = MAC2_CFG_REG; + break; + } + } + + + // disable PHY's AN + if (y==1) + { + mac_port_config &= ~(0x1 << 7); + } + + // enable PHY's AN + if (y==0) + { + mac_port_config |= (0x1 << 7); + } + + switch (port) + { + case 0: + { + MAC0_CFG_REG = mac_port_config; + break; + } + case 1: + { + MAC1_CFG_REG = mac_port_config; + break; + } + case 2: + { + MAC2_CFG_REG = mac_port_config; + break; + } + } +} + +int cns3xxx_std_phy_power_down(int phy_addr, int y) +{ + u16 phy_data = 0; + // power-down or up the PHY + cns3xxx_read_phy(phy_addr, 0, &phy_data); + if (y==1) // down + phy_data |= (0x1 << 11); + if (y==0) // up + phy_data &= (~(0x1 << 11)); + cns3xxx_write_phy(phy_addr, 0, phy_data); + + phy_data=0; + cns3xxx_read_phy(phy_addr, 0, &phy_data); + + return 0; +} + + +#if defined(LINUX_KERNEL) +int cns3xxx_spi_tx_rx_n(u32 tx_data, u32 *rx_data, u32 tx_channel, u32 tx_eof_flag) +{ + u8 cns3xxx_spi_tx_rx(u8 tx_channel, u8 tx_eof, u32 tx_data, u32 * rx_data); + + return cns3xxx_spi_tx_rx(tx_channel, tx_eof_flag, tx_data, rx_data); +} + +int bcm53115M_reg_read(int page, int offset, u8 *buf, int len) +{ + u32 ch = BCM53115_SPI_CHANNEL; + u8 cmd_byte; + u32 dumy_word; + u32 spi_status; + int i; + + /* + * Normal SPI Mode (Command Byte) + * Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 + * 0 1 1 Mode=0 CHIP_ID2 ID1 ID0(lsb) Rd/Wr(0/1) + * + */ + + /* Normal Read Operation */ + /* 1. Issue a normal read command(0x60) to poll the SPIF bit in the + SPI status register(0XFE) to determine the operation can start */ + do + { + cmd_byte = 0x60; + cns3xxx_spi_tx_rx_n(cmd_byte, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(0xFE, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(0x00, &spi_status, ch, 1); + udelay(100); + }while ((spi_status >> ROBO_SPIF_BIT) & 1) ; // wait SPI bit to 0 + + /* 2. Issue a normal write command(0x61) to write the register page value + into the SPI page register(0xFF) */ + cmd_byte = 0x61; + cns3xxx_spi_tx_rx_n(cmd_byte, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(0xFF, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(page, &dumy_word, ch, 1); + + /* 3. Issue a normal read command(0x60) to setup the required RobiSwitch register + address */ + cmd_byte = 0x60; + cns3xxx_spi_tx_rx_n(cmd_byte, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(offset, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(0x00, &dumy_word, ch, 1); + + /* 4. Issue a normal read command(0x60) to poll the RACK bit in the + SPI status register(0XFE) to determine the completion of read */ + do + { + cmd_byte = 0x60; + cns3xxx_spi_tx_rx_n(cmd_byte, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(0xFE, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(0x00, &spi_status, ch, 1); + udelay(100); + }while (((spi_status >> ROBO_RACK_BIT) & 1) == 0); // wait RACK bit to 1 + + /* 5. Issue a normal read command(0x60) to read the specific register's conternt + placed in the SPI data I/O register(0xF0) */ + cmd_byte = 0x60; + cns3xxx_spi_tx_rx_n(cmd_byte, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(0xF0, &dumy_word, ch, 0); + // read content + for (i=0; i> ROBO_SPIF_BIT) & 1) ; // wait SPI bit to 0 + + /* 2. Issue a normal write command(0x61) to write the register page value + into the SPI page register(0xFF) */ + cmd_byte = 0x61; + cns3xxx_spi_tx_rx_n((u32)cmd_byte, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(0xFF, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(page, &dumy_word, ch, 1); + + /* 3. Issue a normal write command(0x61) and write the address of the accessed + register followed by the write content starting from a lower byte */ + cmd_byte = 0x61; + cns3xxx_spi_tx_rx_n((u32)cmd_byte, &dumy_word, ch, 0); + cns3xxx_spi_tx_rx_n(offset, &dumy_word, ch, 0); + // write content + for (i=0; ivid; + bcm53115M_reg_write(0x05, 0x81, (u8*)&wval, 2); + + // fill table content + dwval = 0; + dwval |= (v->forward_map & 0x1FF); + dwval |= ((v->untag_map& 0x1FF) << 9); + bcm53115M_reg_write(0x05, 0x83, (u8*)&wval, 4); + + // write cmd + bval = VLAN_WRITE_CMD; + bval |= (1 << VLAN_START_BIT); + bcm53115M_reg_write(0x05, 0x80, (u8*)&bval, 1); + + // wait cmd complete + while(1) { + bcm53115M_reg_read(0x05, 0x80, (u8*)&bval, 1); + if (((bval >> VLAN_START_BIT) & 1) == 0) break; + } + + return CAVM_OK; +} + +typedef struct bcm_port_cfg_t +{ + u8 link; + u8 fdx; + BCM_PORT_SPEED speed; + u8 rx_flw_ctrl; + u8 tx_flw_ctrl; + u8 ow; +}bcm_port_cfg; + + + +int bcm53115M_mac_port_config(int port, bcm_port_cfg *cfg) +{ + u8 bval = 0; + int page, addr; + + if (cfg->link) bval |= (1<<0); + if (cfg->fdx) bval |= (1<<1); + bval |= ((cfg->speed&0x3) << 2); + if (cfg->rx_flw_ctrl) bval |= (1<<4); + if (cfg->tx_flw_ctrl) bval |= (1<<5); + + if (port == BCM_PORT_IMP) { + bval |= (1<<7); // Use content of this register + page = 0x00; + addr = 0x0E; + }else { + page = 0x00; + addr = 0x58+port; + } + + bcm53115M_reg_write(page, addr, &bval, 1); + + return 0; +} + +int bcm53115M_init_internal_phy(void) +{ + int p, page; + u16 wval; + + for (p=BCM_PORT_0; p<=BCM_PORT_4; p++) { + page = 0x10+p; + + // reset phy + bcm53115M_reg_read(page, 0x00, (u8*)&wval, 2); + wval |= 0x8000; + bcm53115M_reg_write(page, 0x00, (u8*)&wval, 2); + + // config auto-nego & all advertisement + bcm53115M_reg_read(page, 0x00, (u8*)&wval, 2); + wval |= (1<<12); // auto-nego + bcm53115M_reg_write(page, 0x00, (u8*)&wval, 2); + + bcm53115M_reg_read(page, 0x08, (u8*)&wval, 2); + wval |= 0x1E0; // advertisement all + bcm53115M_reg_write(page, 0x08, (u8*)&wval, 2); + + // 1000BASE-T + bcm53115M_reg_read(page, 0x12, (u8*)&wval, 2); + wval &= ~(1<<12); // automatic master/slave configuration + wval |= 0x300; // 1000-base full/half advertisements + bcm53115M_reg_write(page, 0x12, (u8*)&wval, 2); + } + + return 0; +} + +int bcm53115M_led_init(void) +{ + u16 led_func, bval, wval; + + /* LED function 1G/ACT, 100M/ACT, 10M/ACT, not used */ + led_func = 0x2C00; + bcm53115M_reg_write(0x00, 0x10, (u8*)&led_func, 2); + bcm53115M_reg_write(0x00, 0x12, (u8*)&led_func, 2); + + /* LED map enable */ + wval = 0x1F; // port0~4 + bcm53115M_reg_write(0x00, 0x16, (u8*)&wval, 2); + + /* LED mode map */ + wval = 0x1F; // led auto mode + bcm53115M_reg_write(0x00, 0x18, (u8*)&wval, 2); + bcm53115M_reg_write(0x00, 0x1A, (u8*)&wval, 2); + + /* LED enable */ + bcm53115M_reg_read(0x00, 0x0F, (u8*)&bval, 1); + bval |= 0x80; + bcm53115M_reg_write(0x00, 0x0F, (u8*)&bval, 1); + + return 0; +} + +//#define BCM53115M_DUMMY_SWITCH +#define BCM53115M_DISABLE_LEARNING + +int bcm53115M_init(u8 mac_port, u16 phy_addr) +{ + u32 u32_val=0; + u16 u16_val=0; + u8 bval=0; + int i=0; + bcm53115M_vlan_entry v_ent; + bcm_port_cfg pc; + u8 page=0, offset=0; + + printk("bcm53115M init\n"); + + memset(&v_ent, 0, sizeof(bcm53115M_vlan_entry)); + + // gpio B pin 18 + gpio_direction_output(50, 0); + bcm53115M_init_mac(0, 0); + bcm53115M_init_mac(1, 1); + + // read device id + bcm53115M_reg_read(0x02, 0x30, (u8*)&u32_val, 4); + printk("bcm53115M device id:(0x%x)\r\n", u32_val); + + if (u32_val != 0x53115) { + printk("bad device id(0x%x)\r\n", u32_val); + return -1; + } + + u16_val=0; + // read phy id + bcm53115M_reg_read(0x10, 0x04, (u8 *)&u16_val, 2); + printk("bcm53115M phy id_1:(0x%x)\r\n", u16_val); + + if (u16_val != 0x143) { + printk("bad phy id1(0x%x)\r\n", u16_val); + return CAVM_ERR; + } + + u16_val=0; + // read phy id2 + bcm53115M_reg_read(0x10, 0x06, (u8 *)&u16_val, 2); + printk("bcm53115M phy id_2:(0x%x)\r\n", u16_val); + +#ifdef BCM53115M_DUMMY_SWITCH + bval=0; + bcm53115M_reg_read(0x00, 0x0e, (u8 *)&bval, 1); + printk("bcm53115M page:0 addr:0x0e ## %x\n", bval); + bval |= (1 << 7); + bval |= 1; + + bval = 0x8b; + bval |= (1 << 5); + bval |= (1 << 4); + printk("bval : %x\n", bval); + bcm53115M_reg_write(0x00, 0x0e, (u8 *)&bval, 1); + bcm53115M_reg_read(0x00, 0x0e, (u8 *)&bval, 1); + printk("bcm53115M page:0 addr:0x0e ## %x\n", bval); + + /* Unmanagement mode */ + // Switch Mode. Page 00h,Address 0Bh + bval = 0x06; // forward enable, unmanaged mode + //bval = 0x3; // forward enable, managed mode + bcm53115M_reg_write(0x0, 0xb, &bval, 1); + bcm53115M_reg_read(0x0, 0xb, (u8 *)&bval, 1); + printk("bcm53115M page:0 addr:0xb ## %x\n", bval); + + page=0x0; + offset=0x5d; // port 5 + bval=0x7b; + bcm53115M_reg_write(page, offset, (u8 *)&bval, 1); + bcm53115M_reg_read(page, offset, (u8 *)&bval, 1); + + printk("bcm53115M page:%x addr:%x ## %x\n", page, offset, bval); + + page=0x0; + offset=0x58; // port 0 + bval=0x7b; + bcm53115M_reg_write(page, offset, (u8 *)&bval, 1); + bcm53115M_reg_read(page, offset, (u8 *)&bval, 1); + printk("bcm53115M page:%x addr:%x ## %x\n", page, offset, bval); + +#ifdef CONFIG_CNS3XXX_JUMBO_FRAME + printk("enable BCM53115 jumbo frame\n"); + + page=0x40; + offset=0x01; + u32_val=0x013f; // enable 0-5 port and IMP port jumbo frame. MAX frame: 9720 bytes. + bcm53115M_reg_write(page, offset, (u8 *)&u32_val, 4); + bcm53115M_reg_read(page, offset, (u8 *)&u32_val, 4); + printk("bcm53115M page:%x addr:%x ## %x\n", page, offset, u32_val); + +#if 0 + page=0x40; + offset=0x05; + u16_val=0; + bcm53115M_reg_write(page, offset, (u8 *)&u16_val, 2); +#endif + +#endif + +#else // !BCM53115M_DUMMY_SWITCH + /* Loop detection disable */ + bcm53115M_reg_read(0x72, 0x00, (u8 *)&u16_val, 2); + u16_val &= ~(0x3<<11); + bcm53115M_reg_write(0x72, 0x00, (u8 *)&u16_val, 2); + + + /* VLAN forwarding mask */ + // Bit8 IMP port, Bits[5:0] correspond to ports[5:0] + // port 0 <-> port IMP + u16_val = 0x103; + bcm53115M_reg_write(0x31, 0x0, (u8 *)&u16_val, 2); // port 0 + u16_val = 0x103; + bcm53115M_reg_write(0x31, 0x10, (u8 *)&u16_val, 2); // IMP + + + // port 4 <-> port 5 + u16_val = 0x3c; + bcm53115M_reg_write(0x31, 0x08, (u8 *)&u16_val, 2); // port 4 + u16_val = 0x3c; + bcm53115M_reg_write(0x31, 0x0A, (u8 *)&u16_val, 2); // port 5 + + + // others <-> none + u16_val = 0x00; + bcm53115M_reg_write(0x31, 0x02, (u8 *)&u16_val, 2); // port 1 + bcm53115M_reg_write(0x31, 0x04, (u8 *)&u16_val, 2); // port 2 + bcm53115M_reg_write(0x31, 0x06, (u8 *)&u16_val, 2); // port 3 + + // port 1 <-> port IMP + u16_val = 0x103; + bcm53115M_reg_write(0x31, 0x2, (u8 *)&u16_val, 2); // port 1 + + // port 2 <-> port 5 + u16_val = 0x3c; + bcm53115M_reg_write(0x31, 0x4, (u8 *)&u16_val, 2); // port 2 + + // port 3 <-> port 5 + u16_val = 0x3c; + bcm53115M_reg_write(0x31, 0x6, (u8 *)&u16_val, 2); // port 3 + + /* Create VLAN1 for default port pvid */ +#if 0 + v_ent.vid = 1; + v_ent.forward_map = 0x13F; // all ports + robo_write_vlan(&v_ent); +#endif + + /* Unmanagement mode */ + // Switch Mode. Page 00h,Address 0Bh + bval = 0x02; // forward enable, unmanaged mode + bcm53115M_reg_write(0x0, 0xb, &bval, 1); + + /* Init port5 & IMP (test giga mode first) */ + // IMP port control. Page 00h,Address 08h + bval = 0x1C; // RX UCST/MCST/BCST enable + bcm53115M_reg_write(0x0, 0x8, &bval, 1); + + offset=0x5d; // port 5 + bval=0x7b; + bcm53115M_reg_write(page, offset, (u8 *)&bval, 1); + bcm53115M_reg_read(page, offset, (u8 *)&bval, 1); + + // Speed, dulplex......etc + // setting in Gsw_Configure_Gsw_Hardware() + + // Mgmt configuration, Page 02h, Address 00h + bval = 0; + bcm53115M_reg_write(0x02, 0x00, &bval, 1); + // BRCM header, Page 02h, Address 03h + bval = 0; // without additional header information + bcm53115M_reg_write(0x02, 0x03, &bval, 1); + + /* Init front ports, port0-4 */ + // MAC + pc.speed = BCM_PORT_1G; + pc.link = 0; // link detect by robo_port_update() + pc.ow = 0; + for (i=BCM_PORT_0; i<=BCM_PORT_4; i++) + bcm53115M_mac_port_config(i, &pc); + // Internal Phy + bcm53115M_init_internal_phy(); + + /* Enable all port, STP_STATE=No spanning tree, TX/RX enable */ + // Page 00h, Address 00h-05h + bval = 0x0; + for (i=0; i<=5; i++) + bcm53115M_reg_write(0x0, i, &bval, 1); + + // Disable broadcast storm control due to h/w strap pin BC_SUPP_EN + // Page 41h, Address 10h-13h, bit28&22 + + // for port 0 ~ 5 + for (i=0 ; i <= 0x14; i+=4) { + bcm53115M_reg_read(0x41, 0x10+i, (u8 *)&u32_val, 4); + u32_val &= ~((1<<28) | (1<<22)); + bcm53115M_reg_write(0x41, 0x10+i, (u8 *)&u32_val, 4); + } + + // for IMP port + bcm53115M_reg_read(0x41, 0x30, (u8 *)&u32_val, 4); + u32_val &= ~((1<<28) | (1<<22)); + bcm53115M_reg_write(0x41, 0x30, (u8 *)&u32_val, 4); + + /* Misc */ + // led + bcm53115M_led_init(); + // multicast fwd rule, Page 00h, Address 2Fh + bval = 0; + bcm53115M_reg_write(0x00, 0x2F, &bval, 1); + +#ifdef BCM53115M_DISABLE_LEARNING + // disable learning + page=0x00; + offset=0x3c; + u16_val=0x13f; + bcm53115M_reg_write(page, offset, (u8 *)&u16_val, 2); + bcm53115M_reg_read(page, offset, (u8 *)&u16_val, 2); + + page=0x02; + offset=0x06; + u32_val=4; + bcm53115M_reg_write(page, offset, (u8 *)&u32_val, 4); +#endif +#endif + return CAVM_OK; +} +#endif // defined(LINUX_KERNEL) + +//#define MAC2_RGMII +#define CNS3XXX_MAC2_IP1001_GIGA_MODE + +void icp_ip1001_init_mac(u8 mac_port, u16 phy_addr) +{ + u32 mac_port_config = 0; + u8 mac_addr[]={0x0c, 0x10, 0x18}; + + cns3xxx_enable_mac_clock(mac_port, 1); + + mac_port_config = SWITCH_REG_VALUE(mac_addr[mac_port]); + + //cns3xxx_txc_dly(mac_port, 2); + //cns3xxx_rxc_dly(mac_port, 2); + //SLK_SKEW_CTRL_REG +#if 1 + + // enable GMII, MII, reverse MII + mac_port_config &= (~(1 << 15)); + +#ifdef MAC2_RGMII + mac_port_config |= (1 << 15); +#endif + + // TXC check disable + //mac_port_config &= (~(1 << 13)); + + // disable GIGA mode + mac_port_config &= (~(1<<16)); + +#ifdef CNS3XXX_MAC2_IP1001_GIGA_MODE + // enable GIGA mode + mac_port_config |= (1<<16); + + //mac_port_config |= (1<<19); +#endif + + // disable PHY's AN + mac_port_config &= (~(0x1 << 7)); + + // enable PHY's AN + mac_port_config |= (0x1 << 7); +#else + // disable PHY's AN + mac_port_config &= (~(0x1 << 7)); + // disable GIGA mode + mac_port_config &= (~(1<<16)); + + // force 100Mbps + mac_port_config &= (~(0x3 << 8)); + mac_port_config |= (0x1 << 8); + + // force duplex + mac_port_config |= (0x1 << 10); + + // TX flow control off + mac_port_config &= (~(0x1 << 12)); + + // RX flow control off + mac_port_config &= (~(0x1 << 11)); + +#if 0 + // TX flow control on + mac_port_config |= (0x1 << 12); + + // RX flow control on + mac_port_config |= (0x1 << 11); +#endif + + // enable GMII, MII, reverse MII + mac_port_config &= (~(1 << 15)); +#endif + SWITCH_REG_VALUE(mac_addr[mac_port]) = mac_port_config; + + // If mac port AN turns on, auto polling needs to turn on. + cns3xxx_phy_auto_polling_conf(mac_port, phy_addr); + +} + +int icp_ip1001_init(u8 mac_port, u8 phy_addr) +{ + u16 phy_data = 0; + + printk("mac_port: %d ## phy_addr: %d\n", mac_port, phy_addr); + cns3xxx_mdc_mdio_disable(0); + +#if 0 + // GMII2 high speed drive strength + IOCDA_REG &= ((~3 << 10)); + IOCDA_REG |= (1 << 10); +#endif + IOCDA_REG = 0x55555800; + + phy_data = get_phy_id(phy_addr); // should be 0x243 + + printk("ICPLUS IP 1001 phy id : %x\n", phy_data); + + if (phy_data != 0x0243) { + printk("wrong phy id!!\n"); + return CAVM_ERR; + } + + + cns3xxx_phy_reset(phy_addr); + + icp_ip1001_init_mac(mac_port, phy_addr); + + // read advertisement register + cns3xxx_read_phy(phy_addr, 0x4, &phy_data); + + // enable PAUSE frame capability + phy_data |= (0x1 << 10); + + phy_data &= (~(0x1 << 5)); + phy_data &= (~(0x1 << 6)); + phy_data &= (~(0x1 << 7)); + phy_data &= (~(0x1 << 8)); + +#if 1 + phy_data |= (0x1 << 5); + phy_data |= (0x1 << 6); + phy_data |= (0x1 << 7); + phy_data |= (0x1 << 8); +#endif + + cns3xxx_write_phy(phy_addr, 0x4, phy_data); + + cns3xxx_read_phy(phy_addr, 9, &phy_data); + + phy_data &= (~(1<<8)); // remove advertise 1000 half duples + phy_data &= (~(1<<9)); // remove advertise 1000 full duples +#ifdef CNS3XXX_MAC2_IP1001_GIGA_MODE + //phy_data |= (1<<8); // add advertise 1000 half duples + phy_data |= (1<<9); // add advertise 1000 full duples +#endif + cns3xxx_write_phy(phy_addr, 9, phy_data); + + cns3xxx_read_phy(phy_addr, 9, &phy_data); + + cns3xxx_read_phy(phy_addr, 0, &phy_data); + // AN enable + phy_data |= (0x1 << 12); + cns3xxx_write_phy(phy_addr, 0, phy_data); + + cns3xxx_read_phy(phy_addr, 0, &phy_data); + // restart AN + phy_data |= (0x1 << 9); + cns3xxx_write_phy(phy_addr, 0, phy_data); + return 0; +} + +#define PHY_CONTROL_REG_ADDR 0x00 +#define PHY_AN_ADVERTISEMENT_REG_ADDR 0x04 + +int icp_101a_init_mac(u8 port, u8 phy_addr) +{ + u32 mac_port_config = 0; + + cns3xxx_enable_mac_clock(port, 1); + + switch (port) + { + case 0: + { + mac_port_config = MAC0_CFG_REG; + break; + } + case 1: + { + mac_port_config = MAC1_CFG_REG; + break; + } + case 2: + { + mac_port_config = MAC2_CFG_REG; + break; + } + } + + // enable GMII, MII, reverse MII + mac_port_config &= (~(1 << 15)); + + // disable PHY's AN, use force mode + mac_port_config &= (~(0x1 << 7)); +#ifdef CONFIG_FPGA_FORCE + + // force 100Mbps + mac_port_config &= (~(0x3 << 8)); + mac_port_config |= (0x1 << 8); + + // force duplex + mac_port_config |= (0x1 << 10); + + // TX flow control on + mac_port_config |= (0x1 << 12); + + // RX flow control on + mac_port_config |= (0x1 << 11); + + // Turn off GSW_PORT_TX_CHECK_EN_BIT + mac_port_config &= (~(0x1 << 13)); +#else + // enable PHY's AN + mac_port_config |= (0x1 << 7); + // If mac port AN turns on, auto polling needs to turn on. + cns3xxx_phy_auto_polling_conf(port, phy_addr); +#endif + // normal MII + mac_port_config &= (~(1 << 14)); + + + switch (port) + { + case 0: + { + MAC0_CFG_REG = mac_port_config; + break; + } + case 1: + { + MAC1_CFG_REG = mac_port_config; + break; + } + case 2: + { + MAC2_CFG_REG = mac_port_config; + break; + } + } + + + return CAVM_OK; +} + +int icp_101a_init(u8 mac_port, u8 phy_addr) +{ + u32 mac_port_config=0; + u16 phy_data = 0; + + cns3xxx_mdc_mdio_disable(0); + cns3xxx_phy_reset(phy_addr); + + phy_data = get_phy_id(mac_port); + if (phy_data != 0x0243) { + printk("ICPLUS 101A phy id should be 0x243, but the phy id is : %x\n", phy_data); + return CAVM_ERR; + } + printk("phy id : %x\n", phy_data); + printk("init IC+101A\n"); + + icp_101a_init_mac(mac_port, phy_addr); + + // read advertisement register + cns3xxx_read_phy(phy_addr, 0x4, &phy_data); + + // enable PAUSE frame capability + phy_data |= (0x1 << 10); + + cns3xxx_write_phy(phy_addr, 0x4, phy_data); + +#ifndef CONFIG_FPGA_FORCE + + switch (mac_port) + { + case 0: + { + mac_port_config = MAC0_CFG_REG; + break; + } + case 1: + { + mac_port_config = MAC1_CFG_REG; + break; + } + case 2: + { + mac_port_config = MAC2_CFG_REG; + break; + } + } + +#if 0 + if (!(mac_port_config & (0x1 << 5))) { + if (cns3xxx_read_phy (port, PHY_AN_ADVERTISEMENT_REG_ADDR, &phy_data) == CAVM_ERR) + { + //PDEBUG("\n PORT%d, enable local flow control capability Fail\n", port); + return CAVM_ERR; + } + else + { + // enable PAUSE frame capability + phy_data |= (0x1 << 10); + + if (cns3xxx_write_phy (port, PHY_AN_ADVERTISEMENT_REG_ADDR, phy_data) == CAVM_ERR) + { + //PDEBUG("\nPORT%d, enable PAUSE frame capability Fail\n", port); + return CAVM_ERR; + } + } + } +#endif + + cns3xxx_read_phy(phy_addr, 0, &phy_data); + // an enable + phy_data |= (0x1 << 12); + + // restart AN + phy_data |= (0x1 << 9); + cns3xxx_write_phy(phy_addr, 0, phy_data); + + while (1) + { + //PDEBUG ("\n Polling PHY%d AN \n", port); + cns3xxx_read_phy (phy_data, 0, &phy_data); + + if (phy_data & (0x1 << 9)) { + continue; + } else { + //PDEBUG ("\n PHY%d AN restart is complete \n", port); + break; + } + } + +#endif + + return CAVM_OK; +} + +int cns3xxx_config_VSC8601_mac(u8 port) +{ + u32 mac_port_config = 0; + + switch (port) + { + case 0: + { + mac_port_config = MAC0_CFG_REG; + break; + } + case 1: + { + mac_port_config = MAC1_CFG_REG; + break; + } + case 2: + { + mac_port_config = MAC2_CFG_REG; + break; + } + } + + switch (port) + { + case 0: + { + MAC0_CFG_REG = mac_port_config; + break; + } + case 1: + { + MAC1_CFG_REG = mac_port_config; + break; + } + case 2: + { + MAC2_CFG_REG = mac_port_config; + break; + } + } + return CAVM_OK; +} + +u16 get_phy_id(u8 phy_addr) +{ + u16 read_data; + + cns3xxx_read_phy(phy_addr, 2, &read_data); + + return read_data; +} + +u32 get_vsc8601_recv_err_counter(u8 phy_addr) +{ + u16 read_data=0; + cns3xxx_read_phy(phy_addr, 19, &read_data); + return read_data; +} + +u32 get_crc_good_counter(u8 phy_addr) +{ + u16 read_data=0; + + // enter extended register mode + cns3xxx_write_phy(phy_addr, 31, 0x0001); + + cns3xxx_read_phy(phy_addr, 18, &read_data); + + // back to normal register mode + cns3xxx_write_phy(phy_addr, 31, 0x0000); + + return read_data; +} + +int cns3xxx_config_VSC8601(u8 mac_port, u8 phy_addr) +{ + u16 phy_data=0; + u32 mac_port_config=0; + //u8 tx_skew=1, rx_skew=1; + u16 phy_id=0; + + cns3xxx_mdc_mdio_disable(0); + + cns3xxx_read_phy(phy_addr, 0, &phy_data); + // software reset + phy_data |= (0x1 << 15); + cns3xxx_write_phy(phy_addr, 0, phy_data); + udelay(10); + + phy_id = get_phy_id(phy_addr); + if (phy_id != 0x143) { + return CAVM_ERR; + } + + switch (mac_port) + { + case 0: + { + mac_port_config = MAC0_CFG_REG; + break; + } + case 1: + { + mac_port_config = MAC1_CFG_REG; + break; + } + case 2: + { + mac_port_config = MAC2_CFG_REG; + break; + } + } + + cns3xxx_enable_mac_clock(mac_port, 1); + //phy_auto_polling(mac_port, phy_addr); + + // enable RGMII-PHY mode + mac_port_config |= (0x1 << 15); + + // If mac AN turns on, auto polling needs to turn on. + // enable PHY's AN + mac_port_config |= (0x1 << 7); + cns3xxx_phy_auto_polling_conf(mac_port, phy_addr); + + // enable GSW MAC port 0 + mac_port_config &= ~(0x1 << 18); + + // normal MII + mac_port_config &= (~(1 << 14)); + + switch (mac_port) + { + case 0: + { + MAC0_CFG_REG = mac_port_config; + printk("8601 MAC0_CFG_REG: %x\n", MAC0_CFG_REG); + break; + } + case 1: + { + MAC1_CFG_REG = mac_port_config; + printk("8601 MAC1_CFG_REG: %x\n", MAC1_CFG_REG); + break; + } + case 2: + { + MAC2_CFG_REG = mac_port_config; + break; + } + } + + cns3xxx_write_phy(phy_addr, 0x18, 0xf1e7); + cns3xxx_write_phy(phy_addr, 0x1c, 0x8e00); + cns3xxx_write_phy(phy_addr, 0x10, 0x20); + cns3xxx_write_phy(phy_addr, 0x1c, 0xa41f); + cns3xxx_write_phy(phy_addr, 0x1c, 0xb41a); + cns3xxx_write_phy(phy_addr, 0x1c, 0xb863); + cns3xxx_write_phy(phy_addr, 0x17, 0xf04); + cns3xxx_write_phy(phy_addr, 0x15, 0x1); + cns3xxx_write_phy(phy_addr, 0x17, 0x0); + + return CAVM_OK; +} + + + +#ifdef CONFIG_LIBRA +void icp_175c_all_phy_power_down(int y) +{ + int i=0; + + for (i=0 ; i < 5 ; ++i) + std_phy_power_down(i, y); + +} + +static int star_gsw_config_icplus_175c_phy4(void) +{ + u16 phy_data = 0, phy_data2 = 0; + u32 volatile ii, jj; + u8 phy_speed_dup = 0, phy_flowctrl = 0; + u32 volatile reg; + u8 gsw_mac_0_phy_addr = 0; + u8 gsw_mac_1_phy_addr = 1; + + + printk("config IC+175C\n"); + /* + * Configure MAC port 0 + * For IP175C Switch setting + * Force 100Mbps, and full-duplex, and flow control on + */ + reg = GSW_MAC_PORT_0_CONFIG_REG; + + // disable PHY's AN + reg &= ~(0x1 << 7); + + // disable RGMII-PHY mode + reg &= ~(0x1 << 15); + + // force speed = 100Mbps + reg &= ~(0x3 << 8); + reg |= (0x1 << 8); + + // force full-duplex + reg |= (0x1 << 10); + + // force Tx/Rx flow-control on + reg |= (0x1 << 11) | (0x1 << 12); + + GSW_MAC_PORT_0_CONFIG_REG = reg; + + + for (ii = 0; ii < 0x2000; ii++) + { + reg = GSW_MAC_PORT_0_CONFIG_REG; + + if ((reg & 0x1) && !(reg & 0x2)) + { + /* + * enable MAC port 0 + */ + reg &= ~(0x1 << 18); + + + /* + * enable the forwarding of unknown, multicast and broadcast packets to CPU + */ + reg &= ~((0x1 << 25) | (0x1 << 26) | (0x1 << 27)); + + /* + * include unknown, multicast and broadcast packets into broadcast storm + */ + reg |= ((0x1 << 29) | (0x1 << 30) | ((u32)0x1 << 31)); + + GSW_MAC_PORT_0_CONFIG_REG = reg; + + break; + } + else + { + for (jj = 0; jj < 0x1000; jj++); + + + if ((ii % 4) == 0) + printk("\rCheck MAC/PHY 0 Link Status : |"); + else if ((ii % 4) == 1) + printk("\rCheck MAC/PHY 0 Link Status : /"); + else if ((ii % 4) == 2) + printk("\rCheck MAC/PHY 0 Link Status : -"); + else if ((ii % 4) == 3) + printk("\rCheck MAC/PHY 0 Link Status : \\"); + } + } + + + if (!(reg & 0x1) || (reg & 0x2)) + { + /* + * Port 0 PHY link down or no TXC in Port 0 + */ + printk("\rCheck MAC/PHY 0 Link Status : DOWN!\n"); + + return -1; + } + else + { + printk("\rCheck MAC/PHY 0 Link Status : UP!\n"); + } + + + + /* + * Configure MAC port 1 + */ + reg = GSW_MAC_PORT_0_CONFIG_REG; + + // disable MAC's AN + reg &= ~(0x1 << 7); + + GSW_MAC_PORT_0_CONFIG_REG = reg; + + + /* enable flow control on (PAUSE frame) */ + star_gsw_read_phy(gsw_mac_1_phy_addr, 0x4, &phy_data); + + phy_data |= (0x1 << 10); + + star_gsw_write_phy(gsw_mac_1_phy_addr, 0x4, phy_data); + +#if 1 + /* 2007/12/18 Jerry + The software reset of IC+ 175C won't reset MII register 29, 30, 31. + Router Control Register: bit 7 (TAG_VLAN_EN) is a VLAN related filed which affect vlan setting. + Router Control Register: bit 3 (ROUTER_EN) enable router function at MII port. + We set them to default to let U-boot properly work. + */ + phy_data = 0x1001; + star_gsw_write_phy(30, 9, phy_data); +#endif + /* restart PHY auto neg. */ + star_gsw_read_phy(gsw_mac_1_phy_addr, 0x0, &phy_data); + + phy_data |= (0x1 << 9) | (0x1 << 12); + + star_gsw_write_phy(gsw_mac_1_phy_addr, 0x0, phy_data); + + + + /* wait for PHY auto neg. complete */ + for (ii = 0; ii < 0x20; ii++) + { + star_gsw_read_phy(gsw_mac_1_phy_addr, 0x1, &phy_data); + + if ((phy_data & (0x1 << 2)) && (phy_data & (0x1 << 5))) + { + break; + } + else + { + if ((ii % 4) == 0) + printk("\rCheck MAC/PHY 1 Link Status : |"); + else if ((ii % 4) == 1) + printk("\rCheck MAC/PHY 1 Link Status : /"); + else if ((ii % 4) == 2) + printk("\rCheck MAC/PHY 1 Link Status : -"); + else if ((ii % 4) == 3) + printk("\rCheck MAC/PHY 1 Link Status : \\"); + } + } + + + if (ii >= 0x20) + { + printk("\rCheck MAC/PHY 1 Link Status : DOWN!\n"); + + return -1; + } + else + { + printk("\rCheck MAC/PHY 1 Link Status : UP!\n"); + } + + + star_gsw_read_phy(gsw_mac_1_phy_addr, 0x4, &phy_data); + + star_gsw_read_phy(gsw_mac_1_phy_addr, 0x5, &phy_data2); + + + if (phy_data & 0x0400) //FC on + { + //printk(""); + phy_flowctrl = 1; + } + else + { + // printk(""); + phy_flowctrl = 0; + } + + + phy_speed_dup = 0; + + if ((phy_data & 0x0100) && (phy_data2 & 0x0100)) //100F + { + // printk("<100F>"); + phy_speed_dup |= (0x1 << 3); //set bit3 for 100F + } + else if ((phy_data & 0x0080) && (phy_data2 & 0x0080)) //100F + { + // printk("<100H>"); + phy_speed_dup |= (0x1 << 2); + } + else if ((phy_data & 0x0040) && (phy_data2 & 0x0040)) //100F + { + // printk("<10F>"); + phy_speed_dup |= (0x1 << 1); + } + else if ((phy_data & 0x0020) && (phy_data2 & 0x0020)) //100F + { + // printk("<10H>"); + phy_speed_dup |= 0x1; + } + + + /* + * Configure MAC port 1 in forced setting subject to the current PHY status + */ + reg = GSW_MAC_PORT_1_CONFIG_REG; + + reg &= ~(0x1 << 7); //AN off + + reg &= ~(0x3 << 8); + + if (phy_speed_dup & 0x0C) //100 + { + //printk(""); + reg |= (0x01 << 8); + } + else if (phy_speed_dup & 0x03) //10 + { + //printk(""); + reg |= (0x00 << 8); + } + + reg &= ~(0x1 << 11); + + if (phy_flowctrl) //FC on + { + //printk(""); + reg |= (0x1 << 11); + } + else + { + //printk(""); + reg |= (0x0 << 11); + } + + reg &= ~(0x1 << 10); + + if ((phy_speed_dup & 0x2) || (phy_speed_dup & 0x8)) //FullDup + { + //printk(""); + reg |= (0x1 << 10); + } + else //HalfDup + { + //printk(""); + reg |= (0x0 << 10); //Half + } + + GSW_MAC_PORT_1_CONFIG_REG = reg; + + + /* + * Check MAC port 1 link status + */ + for (ii = 0; ii < 0x1000; ii++) + { + reg = GSW_MAC_PORT_1_CONFIG_REG; + + if ((reg & 0x1) && !(reg & 0x2)) + { + /* + * enable MAC port 1 + */ + reg &= ~(0x1 << 18); + + /* + * enable the forwarding of unknown, multicast and broadcast packets to CPU + */ + reg &= ~((0x1 << 25) | (0x1 << 26) | (0x1 << 27)); + + /* + * include unknown, multicast and broadcast packets into broadcast storm + */ + reg |= ((0x1 << 29) | (0x1 << 30) | ((u32)0x1 << 31)); + + GSW_MAC_PORT_1_CONFIG_REG = reg; + + return 0; + } + } + + + if (ii > 0x1000) + { + /* + * Port 1 PHY link down or no TXC in Port 1 + */ + printk("\rCheck MAC/PHY 1 Link Status : DOWN!\n"); + + return -1; + } + return 0; +} +#endif + +#if 0 +static int star_gsw_config_VSC8201(u8 mac_port, u8 phy_addr) // include cicada 8201 +{ + //u32 mac_port_base = 0; + u32 mac_port_config=0; + u16 phy_reg; + int i; + + printk("\nconfigure VSC8201\n"); + //PDEBUG("mac port : %d phy addr : %d\n", mac_port, phy_addr); + /* + * Configure MAC port 0 + * For Cicada CIS8201 single PHY + */ + if (mac_port == 0) { + //PDEBUG("port 0\n"); + mac_port_config = GSW_MAC_PORT_0_CONFIG_REG; + } + if (mac_port == 1) { + //PDEBUG("port 1\n"); + mac_port_config = GSW_MAC_PORT_1_CONFIG_REG; + } + + star_gsw_set_phy_addr(mac_port, phy_addr); + //star_gsw_set_phy_addr(1, 1); + + //mac_port_config = __REG(mac_port_base); + + // enable PHY's AN + mac_port_config |= (0x1 << 7); + + // enable RGMII-PHY mode + mac_port_config |= (0x1 << 15); + + // enable GSW MAC port 0 + mac_port_config &= ~(0x1 << 18); + + if (mac_port == 0) { + //PDEBUG("port 0\n"); + GSW_MAC_PORT_0_CONFIG_REG = mac_port_config; + } + if (mac_port == 1) { + //PDEBUG("port 1\n"); + GSW_MAC_PORT_1_CONFIG_REG = mac_port_config; + } + + /* + * Configure Cicada's CIS8201 single PHY + */ +#ifdef CONFIG_STAR9100_SHNAT_PCI_FASTPATH + /* near-end loopback mode */ + star_gsw_read_phy(phy_addr, 0x0, &phy_reg); + phy_reg |= (0x1 << 14); + star_gsw_write_phy(phy_addr, 0x0, phy_reg); +#endif + + star_gsw_read_phy(phy_addr, 0x1C, &phy_reg); + + // configure SMI registers have higher priority over MODE/FRC_DPLX, and ANEG_DIS pins + phy_reg |= (0x1 << 2); + + star_gsw_write_phy(phy_addr, 0x1C, phy_reg); + + star_gsw_read_phy(phy_addr, 0x17, &phy_reg); + + // enable RGMII MAC interface mode + phy_reg &= ~(0xF << 12); + phy_reg |= (0x1 << 12); + + // enable RGMII I/O pins operating from 2.5V supply + phy_reg &= ~(0x7 << 9); + phy_reg |= (0x1 << 9); + + star_gsw_write_phy(phy_addr, 0x17, phy_reg); + + star_gsw_read_phy(phy_addr, 0x4, &phy_reg); + + // Enable symmetric Pause capable + phy_reg |= (0x1 << 10); + + star_gsw_write_phy(phy_addr, 0x4, phy_reg); + + + + if (mac_port == 0) { + //PDEBUG("port 0\n"); + mac_port_config = GSW_MAC_PORT_0_CONFIG_REG; + } + if (mac_port == 1) { + //PDEBUG("port 1\n"); + mac_port_config = GSW_MAC_PORT_1_CONFIG_REG; + } + + + + + + + + // enable PHY's AN + mac_port_config |= (0x1 << 7); + + if (mac_port == 0) { + //PDEBUG("port 0\n"); + GSW_MAC_PORT_0_CONFIG_REG = mac_port_config; + } + if (mac_port == 1) { + //PDEBUG("port 1\n"); + GSW_MAC_PORT_1_CONFIG_REG = mac_port_config; + } + + /* + * Enable PHY1 AN restart bit to restart PHY1 AN + */ + star_gsw_read_phy(phy_addr, 0x0, &phy_reg); + + phy_reg |= (0x1 << 9) | (0x1 << 12); + + star_gsw_write_phy(phy_addr, 0x0, phy_reg); + + /* + * Polling until PHY0 AN restart is complete + */ + for (i = 0; i < 0x1000; i++) { + star_gsw_read_phy(phy_addr, 0x1, &phy_reg); + + if ((phy_reg & (0x1 << 5)) && (phy_reg & (0x1 << 2))) { + printk("0x1 phy reg: %x\n", phy_reg); + break; + } else { + udelay(100); + } + } + + if (mac_port == 0) { + //PDEBUG("port 0\n"); + mac_port_config = GSW_MAC_PORT_0_CONFIG_REG; + } + if (mac_port == 1) { + //PDEBUG("port 1\n"); + mac_port_config = GSW_MAC_PORT_1_CONFIG_REG; + } + + if (((mac_port_config & 0x1) == 0) || (mac_port_config & 0x2)) { + printk("Check MAC/PHY%s Link Status : DOWN!\n", (mac_port == 0 ? "0" : "1")); + } else { + printk("Check MAC/PHY%s Link Status : UP!\n", (mac_port == 0 ? "0" : "1")); + /* + * There is a bug for CIS8201 PHY operating at 10H mode, and we use the following + * code segment to work-around + */ + star_gsw_read_phy(phy_addr, 0x05, &phy_reg); + + if ((phy_reg & (0x1 << 5)) && (!(phy_reg & (0x1 << 6))) && (!(phy_reg & (0x1 << 7))) && (!(phy_reg & (0x1 << 8)))) { /* 10H,10F/100F/100H off */ + star_gsw_read_phy(phy_addr, 0x0a, &phy_reg); + + if ((!(phy_reg & (0x1 << 10))) && (!(phy_reg & (0x1 << 11)))) { /* 1000F/1000H off */ + star_gsw_read_phy(phy_addr, 0x16, &phy_reg); + + phy_reg |= (0x1 << 13) | (0x1 << 15); // disable "Link integrity check(B13)" & "Echo mode(B15)" + + star_gsw_write_phy(phy_addr, 0x16, phy_reg); + } + } + } + + if (mac_port == 0) { + // adjust MAC port 0 RX/TX clock skew + GSW_BIST_RESULT_TEST_0_REG &= ~((0x3 << 24) | (0x3 << 26)); + GSW_BIST_RESULT_TEST_0_REG |= ((0x2 << 24) | (0x2 << 26)); + } + + if (mac_port == 1) { + // adjust MAC port 1 RX/TX clock skew + GSW_BIST_RESULT_TEST_0_REG &= ~((0x3 << 28) | (0x3 << 30)); + GSW_BIST_RESULT_TEST_0_REG |= ((0x2 << 28) | (0x2 << 30)); + } + + return 0; +} + + + + +static void star_gsw_config_VSC8X01() +{ + u16 phy_id = 0; + +#ifdef CONFIG_DORADO2 + star_gsw_set_phy_addr(1,1); + star_gsw_read_phy(1, 0x02, &phy_id); + // printk("phy id = %X\n", phy_id); + if (phy_id == 0x000F) //VSC8201 + star_gsw_config_VSC8201(1,1); + else + star_gsw_config_VSC8601(1,1); +#else +#ifdef CONFIG_LEO + star_gsw_set_phy_addr(0,0); + star_gsw_read_phy(0, 0x02, &phy_id); + // printk("phy id = %X\n", phy_id); + if (phy_id == 0x000F) //VSC8201 + star_gsw_config_VSC8201(0,0); + else + star_gsw_config_VSC8601(0,0); +#endif +#endif +} +#endif + +#if defined(CONFIG_DORADO) || defined(CONFIG_DORADO2) +static int star_gsw_config_port0_VSC7385(void) +{ + u32 mac_port_config=0; + int i; + + printk("config VSC7385\n"); + + mac_port_config = GSW_MAC_PORT_0_CONFIG_REG; + + // disable PHY's AN + mac_port_config &= ~(0x1 << 7); + + // enable RGMII-PHY mode + mac_port_config |= (0x1 << 15); + + // force speed = 1000Mbps + mac_port_config &= ~(0x3 << 8); + mac_port_config |= (0x2 << 8); + + // force full-duplex + mac_port_config |= (0x1 << 10); + + // force Tx/Rx flow-control on + mac_port_config |= (0x1 << 11) | (0x1 << 12); + + GSW_MAC_PORT_0_CONFIG_REG = mac_port_config; + + udelay(1000); + + for (i = 0; i < 50000; i++) { + mac_port_config = GSW_MAC_PORT_0_CONFIG_REG; + if ((mac_port_config & 0x1) && !(mac_port_config & 0x2)) { + break; + } else { + udelay(100); + } + } + + if (!(mac_port_config & 0x1) || (mac_port_config & 0x2)) { + printk("MAC0 PHY Link Status : DOWN!\n"); + return -1; + } else { + printk("MAC0 PHY Link Status : UP!\n"); + } + + // enable MAC port 0 + mac_port_config &= ~(0x1 << 18); + + // disable SA learning + mac_port_config |= (0x1 << 19); + + // forward unknown, multicast and broadcast packets to CPU + mac_port_config &= ~((0x1 << 25) | (0x1 << 26) | (0x1 << 27)); + + // storm rate control for unknown, multicast and broadcast packets + mac_port_config |= (0x1 << 29) | (0x1 << 30) | ((u32)0x1 << 31); + + GSW_MAC_PORT_0_CONFIG_REG = mac_port_config; + + // disable MAC port 1 + mac_port_config = GSW_MAC_PORT_1_CONFIG_REG; + mac_port_config |= (0x1 << 18); + GSW_MAC_PORT_1_CONFIG_REG = mac_port_config; + + // adjust MAC port 0 /RX/TX clock skew + GSW_BIST_RESULT_TEST_0_REG &= ~((0x3 << 24) | (0x3 << 26)); + GSW_BIST_RESULT_TEST_0_REG |= ((0x2 << 24) | (0x2 << 26)); + + return 0; +} +#endif --- /dev/null +++ b/drivers/net/cns3xxx/cns3xxx_phy.h @@ -0,0 +1,82 @@ +/******************************************************************************* + * + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT +1* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +#ifndef CNS3XXX_PHY_H +#define CNS3XXX_PHY_H + +#define LINUX_KERNEL // if don't define LINUX_KERNEL, mean u-boot + +#if defined(LINUX_KERNEL) +#include +#include +#else // u-boot +#define __init_or_module +#include "cns3xxx_symbol.h" +#endif + +void disable_AN(int port, int y); + +u16 get_phy_id(u8 phy_addr); +int cns3xxx_std_phy_power_down(int phy_addr, int y); +u32 get_vsc8601_recv_err_counter(u8 phy_addr); +u32 get_crc_good_counter(u8 phy_addr); +int cns3xxx_config_VSC8601(u8 mac_port, u8 phy_addr); +int vsc8601_power_down(int phy_addr, int y); +int icp_101a_init(u8 mac_port, u8 phy_addr); +int bcm53115M_init(u8 mac_port, u16 phy_addr); +int icp_ip1001_init(u8 mac_port, u8 phy_addr); + +int cns3xxx_phy_auto_polling_enable(u8 port, u8 en); + +int cns3xxx_read_phy(u8 phy_addr, u8 phy_reg, u16 *read_data); +int cns3xxx_write_phy(u8 phy_addr, u8 phy_reg, u16 write_data); + +// wrap cns3xxx_spi_tx_rx() for argument order +int cns3xxx_spi_tx_rx_n(u32 tx_data, u32 *rx_data, u32 tx_channel, u32 tx_eof_flag); + +// for bcm53115M +#define ROBO_SPIF_BIT 7 +#define BCM53115_SPI_CHANNEL 1 +#define ROBO_RACK_BIT 5 + +#define VLAN_START_BIT 7 +#define VLAN_WRITE_CMD 0 + +//#define BCM_PORT_1G 2 +typedef enum +{ + BCM_PORT_10M = 0, + BCM_PORT_100M, + BCM_PORT_1G, +}BCM_PORT_SPEED; + +#define BCM_PORT_0 0 +#define BCM_PORT_1 1 +#define BCM_PORT_2 2 +#define BCM_PORT_3 3 +#define BCM_PORT_4 4 +#define BCM_PORT_5 5 +#define BCM_PORT_IMP 6 + +#endif // end #ifndef CNS3XXX_PHY_H --- /dev/null +++ b/drivers/net/cns3xxx/cns3xxx_sppe_hook.c @@ -0,0 +1,39 @@ +/****************************************************************************** + * + * Copyright (c) 2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, + * but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or + * visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + * + ******************************************************************************/ + +#if defined(CONFIG_CNS3XXX_SPPE) +#include +#include + +int sppe_hook_ready = 0; +int (*sppe_func_hook)(SPPE_PARAM *param) = NULL; +int sppe_pci_fp_ready = 0; +int (*sppe_pci_fp_hook)(SPPE_PARAM *param) = NULL; + +EXPORT_SYMBOL(sppe_hook_ready); +EXPORT_SYMBOL(sppe_func_hook); +EXPORT_SYMBOL(sppe_pci_fp_ready); +EXPORT_SYMBOL(sppe_pci_fp_hook); + +#endif //#if defined(CONFIG_CNS3XXX_SPPE) + --- /dev/null +++ b/drivers/net/cns3xxx/cns3xxx_symbol.h @@ -0,0 +1,317 @@ +/******************************************************************************* + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +// the symbol define memory map register. + +#ifndef CNS3XXX_SYMBOL_H +#define CNS3XXX_SYMBOL_H + +#define DRV_VERSION "Cavium CNS3XXX Switch Driver-0.0.1" + + +#define LINUX_KERNEL // if don't define LINUX_KERNEL, mean u-boot + +#if defined(LINUX_KERNEL) +// linux kernel +#include + +#define SWITCH_REG_VALUE(offset) (*((volatile unsigned int *)(CNS3XXX_SWITCH_BASE_VIRT+offset))) +#define PMU_REG_VALUE(offset) (*((volatile unsigned int *)(CNS3XXX_PM_BASE_VIRT+offset))) +#define MISC_REG_VALUE(offset) (*((volatile unsigned int *)(CNS3XXX_MISC_BASE_VIRT+offset))) + + +#define NETDEV_SIZE 4097+3 + +#define PORT0_NETDEV_INDEX NETDEV_SIZE-3 +#define PORT1_NETDEV_INDEX NETDEV_SIZE-2 +#define PORT2_NETDEV_INDEX NETDEV_SIZE-1 + +#if defined (CONFIG_CNS3XXX_SPPE) +#define FP_NETDEV_INDEX NETDEV_SIZE-4 +#endif + +#define PORT0_NETDEV net_dev_array[PORT0_NETDEV_INDEX] +#define PORT1_NETDEV net_dev_array[PORT1_NETDEV_INDEX] +#define PORT2_NETDEV net_dev_array[PORT2_NETDEV_INDEX] + +#if defined (CONFIG_CNS3XXX_SPPE) +#define FP_NETDEV net_dev_array[FP_NETDEV_INDEX] +#endif + +#else // u-boot +#include // for u8, u32 + +#include "cns3000.h" +#define CAVM_OK 0 +#define CAVM_ERR 1 +#define CAVM_NOT_FOUND 2 +#define CAVM_FOUND 3 +#define CAVM_FAIL -1 // use minus + +#define SWITCH_REG_VALUE(addr) (*((volatile unsigned int *)(CNS3000_VEGA_SWITCH_BASE+addr))) +#define PMU_REG_VALUE(addr) (*((volatile unsigned int *)(CNS3000_VEGA_PM_BASE+addr))) +#define MISC_REG_VALUE(offset) (*((volatile unsigned int *)(CNS3000_VEGA_MISC_BASE+offset))) + +#endif + +// for VLAN and ARL table MB_PMAP +#define MAC_PORT0_PMAP 1 +#define MAC_PORT1_PMAP (1 << 1) +#define MAC_PORT2_PMAP (1 << 4) +#define CPU_PORT_PMAP (1 << 2) + + + +// memory map register definition + +//#define PHY_CTRL_REG (*(u32 volatile*(0xff))) +#define PHY_CTRL_REG SWITCH_REG_VALUE(0x0) +#define PHY_AUTO_ADDR_REG SWITCH_REG_VALUE(0x04) + +#define MAC_GLOB_CFG_REG SWITCH_REG_VALUE(0x08) +#define MAC_GLOB_CFG_EXT_REG SWITCH_REG_VALUE(0xf4) +#define MAC0_CFG_REG SWITCH_REG_VALUE(0x0c) +#define MAC1_CFG_REG SWITCH_REG_VALUE(0x10) +#define MAC2_CFG_REG SWITCH_REG_VALUE(0x18) +#define CPU_CFG_REG SWITCH_REG_VALUE(0x14) + +#define MAC0_PRI_CTRL_REG SWITCH_REG_VALUE(0x1c) +#define MAC1_PRI_CTRL_REG SWITCH_REG_VALUE(0x20) +#define CPU_PRI_CTRL_REG SWITCH_REG_VALUE(0x24) +#define HNAT_PRI_CTRL_REG SWITCH_REG_VALUE(0x28) +#define MAC2_PRI_CTRL_REG SWITCH_REG_VALUE(0x2c) + +#define MAC0_PRI_CTRL_EXT_REG SWITCH_REG_VALUE(0x30) + +#define ETYPE1_ETYPE0_REG SWITCH_REG_VALUE(0x34) +#define ETYPE3_ETYPE2_REG SWITCH_REG_VALUE(0x38) + +#define UDP_RANGE0_REG SWITCH_REG_VALUE(0x3c) +#define UDP_RANGE1_REG SWITCH_REG_VALUE(0x40) +#define UDP_RANGE2_REG SWITCH_REG_VALUE(0x44) +#define UDP_RANGE3_REG SWITCH_REG_VALUE(0x48) + + +#define PRIO_ETYPE_UDP_REG SWITCH_REG_VALUE(0x4c) + +#define PRIO_IPDSCP_7_0_REG SWITCH_REG_VALUE(0x50) +#define PRIO_IPDSCP_15_8_REG SWITCH_REG_VALUE(0x54) +#define PRIO_IPDSCP_23_16_REG SWITCH_REG_VALUE(0x58) +#define PRIO_IPDSCP_31_24_REG SWITCH_REG_VALUE(0x5c) +#define PRIO_IPDSCP_39_32_REG SWITCH_REG_VALUE(0x60) +#define PRIO_IPDSCP_47_40_REG SWITCH_REG_VALUE(0x64) +#define PRIO_IPDSCP_55_48_REG SWITCH_REG_VALUE(0x68) +#define PRIO_IPDSCP_63_56_REG SWITCH_REG_VALUE(0x6c) + +#define TC_CTRL_REG SWITCH_REG_VALUE(0x70) +#define RATE_CTRL_REG SWITCH_REG_VALUE(0x74) + +#define FC_GLOB_THRS_REG SWITCH_REG_VALUE(0x78) +#define FC_PORT_THRS_REG SWITCH_REG_VALUE(0x7c) +#define MC_GLOB_THRS_REG SWITCH_REG_VALUE(0x80) +#define DC_GLOB_THRS_REG SWITCH_REG_VALUE(0x84) + +#define ARL_VLAN_CMD_REG SWITCH_REG_VALUE(0x88) + +#define ARL_CTRL0_REG SWITCH_REG_VALUE(0x8c) +#define ARL_CTRL1_REG SWITCH_REG_VALUE(0x90) +#define ARL_CTRL2_REG SWITCH_REG_VALUE(0x94) + +#define VLAN_CFG SWITCH_REG_VALUE(0x098) + +#define MAC1_MAC0_PVID_REG SWITCH_REG_VALUE(0x9c) +#define MAC2_CPU_PVID_REG SWITCH_REG_VALUE(0xa0) + +#define VLAN_CTRL0_REG SWITCH_REG_VALUE(0xa4) +#define VLAN_CTRL1_REG SWITCH_REG_VALUE(0xa8) +#define VLAN_CTRL2_REG SWITCH_REG_VALUE(0xac) + +#define SESSION_ID_1_0_REG SWITCH_REG_VALUE(0xb0) +#define SESSION_ID_3_2_REG SWITCH_REG_VALUE(0xb4) +#define SESSION_ID_5_4_REG SWITCH_REG_VALUE(0xb8) +#define SESSION_ID_7_6_REG SWITCH_REG_VALUE(0xbc) +#define SESSION_ID_9_8_REG SWITCH_REG_VALUE(0xc0) +#define SESSION_ID_11_10_REG SWITCH_REG_VALUE(0xc4) +#define SESSION_ID_13_12_REG SWITCH_REG_VALUE(0xc8) +#define SESSION_ID_15_14_REG SWITCH_REG_VALUE(0xcc) + +#define INTR_STAT_REG SWITCH_REG_VALUE(0xd0) +#define INTR_MASK_REG SWITCH_REG_VALUE(0xd4) + +#define SRAM_TEST_REG SWITCH_REG_VALUE(0xd8) + +#define MEM_QUEUE_REG SWITCH_REG_VALUE(0xdc) + +#define SARL_CTRL_REG SWITCH_REG_VALUE(0xe0) +#define SARL_OQ_GTH_REG SWITCH_REG_VALUE(0xe4) +#define SARL_OQ_YTH_REG SWITCH_REG_VALUE(0xe8) +#define SARL_OQ_RTH_REG SWITCH_REG_VALUE(0xec) + +#define SLK_SKEW_CTRL_REG SWITCH_REG_VALUE(0xf0) + +#define DMA_RING_CTRL_REG SWITCH_REG_VALUE(0x100) + +#define DMA_AUTO_POLL_CFG_REG SWITCH_REG_VALUE(0x104) + +#define DELAY_INTR_CFG_REG SWITCH_REG_VALUE(0x108) + +#define TS_DMA_CTRL0_REG SWITCH_REG_VALUE(0x110) +#define TS_DESC_PTR0_REG SWITCH_REG_VALUE(0x114) +#define TS_DESC_BASE_ADDR0_REG SWITCH_REG_VALUE(0x118) + +#define FS_DMA_CTRL0_REG SWITCH_REG_VALUE(0x120) +#define FS_DESC_PTR0_REG SWITCH_REG_VALUE(0x124) +#define FS_DESC_BASE_ADDR0_REG SWITCH_REG_VALUE(0x128) + +#define TS_DMA_CTRL1_REG SWITCH_REG_VALUE(0x130) +#define TS_DESC_PTR1_REG SWITCH_REG_VALUE(0x134) +#define TS_DESC_BASE_ADDR1_REG SWITCH_REG_VALUE(0x138) + +#define FS_DMA_CTRL1_REG SWITCH_REG_VALUE(0x140) +#define FS_DESC_PTR1_REG SWITCH_REG_VALUE(0x144) +#define FS_DESC_BASE_ADDR1_REG SWITCH_REG_VALUE(0x148) + +#define TS_DMA_STA_REG SWITCH_REG_VALUE(0x150) +#define FS_DMA_STA_REG SWITCH_REG_VALUE(0x154) + +#define TS_MRD_CMD_CNT_REG SWITCH_REG_VALUE(0x158) +#define TS_MWT_CMD_CNT_REG SWITCH_REG_VALUE(0x15c) + +#define FS_MRD_CMD_CNT_REG SWITCH_REG_VALUE(0x160) +#define FS_MWT_CMD_CNT_REG SWITCH_REG_VALUE(0x164) + +#define C_RXOKPKT_MAC0_REG SWITCH_REG_VALUE(0x300) +#define C_RXOKBYTE_MAC0_REG SWITCH_REG_VALUE(0x304) +#define C_RXRUNT_MAC0_REG SWITCH_REG_VALUE(0x308) +#define C_RXLONG_MAC0_REG SWITCH_REG_VALUE(0x30c) +#define C_RXDROP_MAC0_REG SWITCH_REG_VALUE(0x310) +#define C_RXCRC_MAC0_REG SWITCH_REG_VALUE(0x314) +#define C_RXARLDROP_MAC0_REG SWITCH_REG_VALUE(0x318) +#define C_VIDROP_MAC0_REG SWITCH_REG_VALUE(0x31c) +#define C_VEDROP_MAC0_REG SWITCH_REG_VALUE(0x320) +#define C_RXRL_MAC0_REG SWITCH_REG_VALUE(0x324) +#define C_RXPAUSE_MAC0_REG SWITCH_REG_VALUE(0x328) + +#define C_TXOKPKT_MAC0_REG SWITCH_REG_VALUE(0x32c) +#define C_TXOKBYTE_MAC0_REG SWITCH_REG_VALUE(0x330) +#define C_TXPAUSECOL_MAC0_REG SWITCH_REG_VALUE(0x334) + +#define C_RXOKPKT_MAC1_REG SWITCH_REG_VALUE(0x400) +#define C_RXOKBYTE_MAC1_REG SWITCH_REG_VALUE(0x404) +#define C_RXRUNT_MAC1_REG SWITCH_REG_VALUE(0x408) +#define C_RXLONG_MAC1_REG SWITCH_REG_VALUE(0x40c) +#define C_RXDROP_MAC1_REG SWITCH_REG_VALUE(0x410) +#define C_RXCRC_MAC1_REG SWITCH_REG_VALUE(0x414) +#define C_RXARLDROP_MAC1_REG SWITCH_REG_VALUE(0x418) +#define C_VIDROP_MAC1_REG SWITCH_REG_VALUE(0x41c) +#define C_VEDROP_MAC1_REG SWITCH_REG_VALUE(0x420) +#define C_RXRL_MAC1_REG SWITCH_REG_VALUE(0x424) +#define C_RXPAUSE_MAC1_REG SWITCH_REG_VALUE(0x428) + +#define C_TXOKPKT_MAC1_REG SWITCH_REG_VALUE(0x42c) +#define C_TXOKBYTE_MAC1_REG SWITCH_REG_VALUE(0x430) +#define C_TXPAUSECOL_MAC1_REG SWITCH_REG_VALUE(0x434) + +#define C_TSOKPKT_CPU_REG SWITCH_REG_VALUE(0x500) +#define C_TSOKBYTE_CPU_REG SWITCH_REG_VALUE(0x504) +#define C_TSRUNT_CPU_REG SWITCH_REG_VALUE(0x508) +#define C_TSLONG_CPU_REG SWITCH_REG_VALUE(0x50c) +#define C_TSNODSTDROP_CPU_REG SWITCH_REG_VALUE(0x510) +#define C_TSARLDROP_CPU_REG SWITCH_REG_VALUE(0x514) +#define C_TSVIDROP_CPU_REG SWITCH_REG_VALUE(0x518) +#define C_TSVEDROP_CPU_REG SWITCH_REG_VALUE(0x51c) +#define C_TSRL_CPU_REG SWITCH_REG_VALUE(0x520) + +#define C_FSOKPKT_CPU_REG SWITCH_REG_VALUE(0x524) +#define C_FSOKBYTE_CPU_REG SWITCH_REG_VALUE(0x528) + +#define C_RXOKPKT_MAC2_REG SWITCH_REG_VALUE(0x600) +#define C_RXOKBYTE_MAC2_REG SWITCH_REG_VALUE(0x604) +#define C_RXRUNT_MAC2_REG SWITCH_REG_VALUE(0x608) +#define C_RXLONG_MAC2_REG SWITCH_REG_VALUE(0x60c) +#define C_RXDROP_MAC2_REG SWITCH_REG_VALUE(0x610) +#define C_RXCRC_MAC2_REG SWITCH_REG_VALUE(0x614) +#define C_RXARLDROP_MAC2_REG SWITCH_REG_VALUE(0x618) +#define C_VIDROP_MAC2_REG SWITCH_REG_VALUE(0x61c) +#define C_VEDROP_MAC2_REG SWITCH_REG_VALUE(0x620) +#define C_RXRL_MAC2_REG SWITCH_REG_VALUE(0x624) +#define C_RXPAUSE_MAC2_REG SWITCH_REG_VALUE(0x628) + +#define C_TXOKPKT_MAC2_REG SWITCH_REG_VALUE(0x62c) +#define C_TXOKBYTE_MAC2_REG SWITCH_REG_VALUE(0x630) +#define C_TXPAUSECOL_MAC2_REG SWITCH_REG_VALUE(0x634) + +#define C_TXOKPKT_MAC0_EXT_REG SWITCH_REG_VALUE(0x72c) +#define C_TXOKBYTE_MAC0_EXT_REG SWITCH_REG_VALUE(0x730) + +#define CLK_GATE_REG PMU_REG_VALUE(0x0) +#define SOFT_RST_REG PMU_REG_VALUE(0x4) +#define PLL_HM_PD_CTRL_REG PMU_REG_VALUE(0x1c) + +#define GPIOB_PIN_EN_REG MISC_REG_VALUE(0x18) +#define IOCDA_REG MISC_REG_VALUE(0x1c) + +#define LEVEL_HIGH 0 +#define RISING_EDGE 1 + +#ifdef CONFIG_SILICON + +#define STATUS_INTERRUPT_ID 49 + +#define FSRC_RING0_INTERRUPT_ID 51 +#define FSQF_RING0_INTERRUPT_ID 53 + +#define FSRC_RING1_INTERRUPT_ID 55 +#define FSQF_RING1_INTERRUPT_ID 57 + +#define TSTC_RING0_INTERRUPT_ID 50 + +#define TSTC_RING1_INTERRUPT_ID 54 + +#define HNAT_INTERRUPT_ID 58 + +#else + +//#define STATUS_INTERRUPT_ID 49 +#define STATUS_INTERRUPT_ID 38 +//#define FSRC_RING0_INTERRUPT_ID 51 +#define FSRC_RING0_INTERRUPT_ID 40 + +#define TSQE_RING0_INTERRUPT_ID 52 + +//#define FSQF_RING0_INTERRUPT_ID 53 +#define FSQF_RING0_INTERRUPT_ID 42 + +#define FSQF_RING1_INTERRUPT_ID 46 +#define FSRC_RING1_INTERRUPT_ID 44 + +//#define FSRC_RING1_INTERRUPT_ID 55 + +#define TSTC_RING0_INTERRUPT_ID 39 +#define TSTC_RING1_INTERRUPT_ID 43 + +#define TSQE_RING1_INTERRUPT_ID 56 +#define HNAT_INTERRUPT_ID 58 +#endif // #ifdef CONFIG_SILICON + +#endif --- /dev/null +++ b/drivers/net/cns3xxx/cns3xxx_tool.h @@ -0,0 +1,898 @@ +/******************************************************************************* + * + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +#ifndef CNS3XXX_TOOL_H +#define CNS3XXX_TOOL_H + +#define PRINT_INFO printk + +#if defined(__KERNEL__) + +#include "cns3xxx.h" +#include // for printk + +#else // u-boot + +#endif + +#define SHOW_DEBUG_MESSAGE +#ifdef SHOW_DEBUG_MESSAGE + +extern int MSG_LEVEL; + +#define NO_MSG 0 +#define NORMAL_MSG 1 +#define WARNING_MSG (1 << 1) +#define CRITICAL_MSG (1 << 2) +#define DUMP_RX_PKT_INFO (1 << 3) +#define DUMP_TX_PKT_INFO (1 << 4) + +#define DEBUG_MSG(msg_level, fmt, args...)\ +{ \ + int i=0; \ +\ + for(i=0 ; i < 3 ; ++i) { \ + if ((MSG_LEVEL & msg_level) >> i) \ + printk(KERN_INFO "*cns3xxx gsw debug* " fmt, ## args); \ + } \ +} + +#endif + +#define GET_MAC_PORT_CFG(port, cfg) \ +{ \ + switch (port) \ + { \ + case MAC_PORT0: \ + { \ + cfg = MAC0_CFG_REG; \ + break; \ + } \ + case MAC_PORT1: \ + { \ + cfg = MAC1_CFG_REG; \ + break; \ + } \ + case MAC_PORT2: \ + { \ + cfg = MAC2_CFG_REG; \ + break; \ + } \ + } \ +} + +#define SET_MAC_PORT_CFG(port, cfg) \ +{ \ + switch (port) \ + { \ + case MAC_PORT0: \ + { \ + MAC0_CFG_REG = cfg; \ + break; \ + } \ + case MAC_PORT1: \ + { \ + MAC1_CFG_REG = cfg; \ + break; \ + } \ + case MAC_PORT2: \ + { \ + MAC2_CFG_REG = cfg; \ + break; \ + } \ + } \ +} + +#define between(x, start, end) ((x)>=(start) && (x)<=(end)) +static inline void print_packet(unsigned char *data, int len) +{ + int i,j; + + printk("packet length: %d%s:\n", len, len>128?"(only show the first 128 bytes)":""); +#if 0 + if(len > 128) { + len = 128; + } +#endif + for(i=0;len;) { + if(len >=16 ) { + for(j=0;j<16;j++) { + printk("%02x ", data[i++]); + } + printk("| "); + + i -= 16; + for(j=0;j<16;j++) { + if( between(data[i], 0x21, 0x7e) ) { + printk("%c", data[i++]); + } + else { + printk("."); + i++; + } + } + printk("\n"); + + len -= 16; + } + else { + /* last line */ + + for(j=0; jvid; + ARL_VLAN_CMD_REG |= (1 << 8); // look up vlan table command + + // wait for vlan command complete + while(( (ARL_VLAN_CMD_REG >> 9) & 1) == 0) ; + + if (!((ARL_VLAN_CMD_REG >> 10) & 1)) { + // not found any entry + return CAVM_NOT_FOUND; + } + + entry->valid = ((VLAN_CTRL0_REG >> 31) & 0x1); + entry->vid = ((VLAN_CTRL2_REG >> 31) & 0xfff); + entry->wan_side = ((VLAN_CTRL0_REG >> 30) & 0x1); + entry->etag_pmap = ((VLAN_CTRL0_REG >> 25) & 0x1f); + entry->mb_pmap = ((VLAN_CTRL0_REG >> 9) & 0x1f); + + entry->my_mac[0] = ((VLAN_CTRL1_REG >> 24) & 0xff); + entry->my_mac[1] = ((VLAN_CTRL1_REG >> 16) & 0xff); + entry->my_mac[2] = ((VLAN_CTRL1_REG >> 8) & 0xff); + entry->my_mac[3] = (VLAN_CTRL1_REG & 0xff); + + entry->my_mac[4] = ((VLAN_CTRL2_REG >> 24) & 0xff); + entry->my_mac[5] = ((VLAN_CTRL2_REG >> 16) & 0xff); + + return CAVM_FOUND; +} + +static inline int cns3xxx_vlan_table_read(VLANTableEntry *entry) +{ + //printf("VLAN_CTRL0_REG: %x\n", VLAN_CTRL0_REG); + ARL_VLAN_CMD_REG &= (~0x3f); + ARL_VLAN_CMD_REG |= (entry->vlan_index); + ARL_VLAN_CMD_REG |= (1 << 7); // read vlan table command + //printf("after read ARL_VLAN_CMD_REG: %x\n", ARL_VLAN_CMD_REG); + + // wait for vlan command complete + while(( (ARL_VLAN_CMD_REG >> 9) & 1) == 0) ; + + //printf("ARL_VLAN_CMD_REG: %x\n", ARL_VLAN_CMD_REG); + //printf("VLAN_CTRL0_REG: %x\n", VLAN_CTRL0_REG); + + entry->valid = ((VLAN_CTRL0_REG >> 31) & 0x1); + entry->vid = ((VLAN_CTRL2_REG) & 0xfff); + entry->wan_side = ((VLAN_CTRL0_REG >> 30) & 0x1); + entry->etag_pmap = ((VLAN_CTRL0_REG >> 25) & 0x1f); + entry->mb_pmap = ((VLAN_CTRL0_REG >> 9) & 0x1f); + + entry->my_mac[0] = ((VLAN_CTRL1_REG >> 24) & 0xff); + entry->my_mac[1] = ((VLAN_CTRL1_REG >> 16) & 0xff); + entry->my_mac[2] = ((VLAN_CTRL1_REG >> 8) & 0xff); + entry->my_mac[3] = (VLAN_CTRL1_REG & 0xff); + + entry->my_mac[4] = ((VLAN_CTRL2_REG >> 24) & 0xff); + entry->my_mac[5] = ((VLAN_CTRL2_REG >> 16) & 0xff); + + return CAVM_OK; + +} + + +// add a entry in the vlan table +static inline int cns3xxx_vlan_table_add(VLANTableEntry *entry) +{ + VLAN_CTRL0_REG = 0; + VLAN_CTRL1_REG = 0; + VLAN_CTRL2_REG = 0; + +#if 0 + printk("a [kernel mode] VLAN_CTRL0_REG: %x\n", VLAN_CTRL0_REG); + printk("a [kernel mode] VLAN_CTRL1_REG: %x\n", VLAN_CTRL1_REG); + printk("a [kernel mode] VLAN_CTRL2_REG: %x\n", VLAN_CTRL2_REG); +#endif + + //printk("vlan_index: %x\n", entry->vlan_index); + VLAN_CTRL0_REG |= (entry->valid << 31); + //DEBUG_MSG(NORMAL_MSG, "1 [kernel mode] VLAN_CTRL0_REG: %x\n", VLAN_CTRL0_REG); + VLAN_CTRL0_REG |= (entry->wan_side << 30); + //DEBUG_MSG(NORMAL_MSG, "2 [kernel mode] VLAN_CTRL0_REG: %x\n", VLAN_CTRL0_REG); + //printk("entry->etag_pmap: %x\n", entry->etag_pmap); + VLAN_CTRL0_REG |= (entry->etag_pmap << 25); + //DEBUG_MSG(NORMAL_MSG, "3 [kernel mode] VLAN_CTRL0_REG: %x\n", VLAN_CTRL0_REG); + //printk("entry->mb_pmap: %x\n", entry->mb_pmap); + VLAN_CTRL0_REG |= (entry->mb_pmap << 9); + //DEBUG_MSG(NORMAL_MSG, "4 [kernel mode] VLAN_CTRL0_REG: %x\n", VLAN_CTRL0_REG); + //printk("bb [kernel mode] VLAN_CTRL0_REG: %x\n", VLAN_CTRL0_REG); + + //printf("vlan index: %d ## add VLAN_CTRL0_REG: %x\n", entry->vlan_index, VLAN_CTRL0_REG); + + + VLAN_CTRL1_REG |= (entry->my_mac[0] << 24); + VLAN_CTRL1_REG |= (entry->my_mac[1] << 16); + VLAN_CTRL1_REG |= (entry->my_mac[2] << 8); + VLAN_CTRL1_REG |= (entry->my_mac[3]); + + VLAN_CTRL2_REG |= (entry->my_mac[4] << 24); + VLAN_CTRL2_REG |= (entry->my_mac[5] << 16); + VLAN_CTRL2_REG |= entry->vid; + +#if 0 + printk("b [kernel mode] VLAN_CTRL0_REG: %x\n", VLAN_CTRL0_REG); + printk("b [kernel mode] VLAN_CTRL1_REG: %x\n", VLAN_CTRL1_REG); + printk("b [kernel mode] VLAN_CTRL2_REG: %x\n", VLAN_CTRL2_REG); +#endif + + ARL_VLAN_CMD_REG &= (~0x3f); + ARL_VLAN_CMD_REG |= (entry->vlan_index); + ARL_VLAN_CMD_REG |= (1 << 6); // write vlan table command + + + //printf("after write ARL_VLAN_CMD_REG: %x\n", ARL_VLAN_CMD_REG); + + // wait for vlan command complete + while(( (ARL_VLAN_CMD_REG >> 9) & 1) == 0) ; + + return CAVM_OK; +} + +static inline void print_arl_table_entry(ARLTableEntry *entry) +{ + printk("vid: %d\n", entry->vid); + printk("pmap: %#x\n", entry->pmap); + printk("age_field: %d\n", entry->age_field); + printk("vlan_mac: %d\n", entry->vlan_mac); + printk("filter: %d\n", entry->filter); + printk("mac addr: %x:%x:%x:%x:%x:%x\n", entry->mac[0], entry->mac[1],entry->mac[2],entry->mac[3],entry->mac[4],entry->mac[5]); + +} + + +static inline int cns3xxx_arl_table_lookup(ARLTableEntry *entry) +{ + ARL_CTRL0_REG = 0; + ARL_CTRL1_REG = 0; + ARL_CTRL2_REG = 0; + + ARL_CTRL0_REG |= (entry->vid << 16); + + ARL_CTRL1_REG |= (entry->mac[0] << 24); + ARL_CTRL1_REG |= (entry->mac[1] << 16); + ARL_CTRL1_REG |= (entry->mac[2] << 8); + ARL_CTRL1_REG |= entry->mac[3]; + + ARL_CTRL2_REG |= (entry->mac[4] << 24); + ARL_CTRL2_REG |= (entry->mac[5] << 16); + + ARL_VLAN_CMD_REG |= (1 << 18); // arl table lookup command + + // wait arl command complete + while(( (ARL_VLAN_CMD_REG >> 21) & 1) == 0); + + if (( (ARL_VLAN_CMD_REG >> 23) & 1)) { + // found + + entry->vid = ((ARL_CTRL0_REG >> 16) & 0xfff); + entry->pmap = ((ARL_CTRL0_REG >> 9) & 0x1f); + + entry->age_field = ((ARL_CTRL2_REG >> 4 ) & 0x7); + entry->vlan_mac = ((ARL_CTRL2_REG >> 1 ) & 0x1); + entry->filter = (ARL_CTRL2_REG & 0x1); + } else { + // not found + return CAVM_NOT_FOUND; + } +#if 0 + printk("[kernel mode] ARL_VLAN_CMD_REG : %#x\n", ARL_VLAN_CMD_REG); + printk("[kernel mode] ARL_CTRL0_REG : %#x\n", ARL_CTRL0_REG); + printk("[kernel mode] ARL_CTRL1_REG : %#x\n", ARL_CTRL1_REG); + printk("[kernel mode] ARL_CTRL2_REG : %#x\n", ARL_CTRL2_REG); +#endif + + return CAVM_FOUND; +} + +static inline int cns3xxx_arl_table_search_again(ARLTableEntry *entry) +{ + ARL_CTRL0_REG = 0; + ARL_CTRL1_REG = 0; + ARL_CTRL2_REG = 0; + + ARL_VLAN_CMD_REG |= (1 << 17); // arl table search again command + + // wait arl command complete + while(( (ARL_VLAN_CMD_REG >> 21) & 1) == 0); + + if ((ARL_VLAN_CMD_REG >> 23) & 1) { + + // found + #if 0 + printk("[kernel mode] ARL_VLAN_CMD_REG : %#x\n", ARL_VLAN_CMD_REG); + printk("[kernel mode] ARL_CTRL0_REG : %#x\n", ARL_CTRL0_REG); + printk("[kernel mode] ARL_CTRL1_REG : %#x\n", ARL_CTRL1_REG); + printk("[kernel mode] ARL_CTRL2_REG : %#x\n", ARL_CTRL2_REG); + #endif + entry->vid = ((ARL_CTRL0_REG >> 16) & 0xfff); + entry->pmap = ((ARL_CTRL0_REG >> 9) & 0x1f); + + entry->age_field = ((ARL_CTRL2_REG >> 4 ) & 0x7); + entry->vlan_mac = ((ARL_CTRL2_REG >> 1 ) & 0x1); + entry->filter = (ARL_CTRL2_REG & 0x1); + + entry->mac[0] = (ARL_CTRL1_REG >> 24); + entry->mac[1] = (ARL_CTRL1_REG >> 16); + entry->mac[2] = (ARL_CTRL1_REG >> 8); + entry->mac[3] = ARL_CTRL1_REG; + + entry->mac[4] = (ARL_CTRL2_REG >> 24); + entry->mac[5] = (ARL_CTRL2_REG >> 16); + + return CAVM_FOUND; + } else { + // not found + return CAVM_NOT_FOUND; + } +} + +static inline int cns3xxx_is_arl_table_end(void) +{ + ARL_CTRL0_REG = 0; + ARL_CTRL1_REG = 0; + ARL_CTRL2_REG = 0; + + if (( (ARL_VLAN_CMD_REG >> 22) & 1)) { // search to table end + return CAVM_OK; + } else { + return CAVM_ERR; + } +} + +static inline int cns3xxx_arl_table_search(ARLTableEntry *entry) +{ + ARL_CTRL0_REG = 0; + ARL_CTRL1_REG = 0; + ARL_CTRL2_REG = 0; + +#if 0 + ARL_CTRL0_REG |= (entry->vid << 16); + + ARL_CTRL1_REG |= (entry->mac[0] << 24); + ARL_CTRL1_REG |= (entry->mac[1] << 16); + ARL_CTRL1_REG |= (entry->mac[2] << 8); + ARL_CTRL1_REG |= entry->mac[3]; + + ARL_CTRL2_REG |= (entry->mac[4] << 24); + ARL_CTRL2_REG |= (entry->mac[5] << 16); +#endif + ARL_VLAN_CMD_REG |= (1 << 16); // arl table search start command + + // wait arl command complete + while(( (ARL_VLAN_CMD_REG >> 21) & 1) == 0); + + if (((ARL_VLAN_CMD_REG >> 23) & 1)) { + // found + #if 0 + printk("[kernel mode] ARL_VLAN_CMD_REG : %#x\n", ARL_VLAN_CMD_REG); + printk("[kernel mode] ARL_CTRL0_REG : %#x\n", ARL_CTRL0_REG); + printk("[kernel mode] ARL_CTRL1_REG : %#x\n", ARL_CTRL1_REG); + printk("[kernel mode] ARL_CTRL2_REG : %#x\n", ARL_CTRL2_REG); + #endif + entry->vid = ((ARL_CTRL0_REG >> 16) & 0xfff); + entry->pmap = ((ARL_CTRL0_REG >> 9) & 0x1f); + + entry->age_field = ((ARL_CTRL2_REG >> 4 ) & 0x7); + entry->vlan_mac = ((ARL_CTRL2_REG >> 1 ) & 0x1); + entry->filter = (ARL_CTRL2_REG & 0x1); + + entry->mac[0] = (ARL_CTRL1_REG >> 24); + entry->mac[1] = (ARL_CTRL1_REG >> 16); + entry->mac[2] = (ARL_CTRL1_REG >> 8); + entry->mac[3] = ARL_CTRL1_REG; + + entry->mac[4] = (ARL_CTRL2_REG >> 24); + entry->mac[5] = (ARL_CTRL2_REG >> 16); + + return CAVM_FOUND; + } else { + // not found + return CAVM_NOT_FOUND; + } +} + + +// flush all age out entries except static entries +static inline int cns3xxx_arl_table_flush(void) +{ + ARL_VLAN_CMD_REG |= (1 << 20); // flush arl table command + + // wait arl command complete + while(( (ARL_VLAN_CMD_REG >> 21) & 1) == 0); + + + return CAVM_OK; +} + + +// add a entry in the arl table +static inline int cns3xxx_arl_table_add(ARLTableEntry *entry) +{ + ARL_CTRL0_REG = 0; + ARL_CTRL1_REG = 0; + ARL_CTRL2_REG = 0; + + entry->age_field = 7; // static entry + ARL_CTRL0_REG |= (entry->vid << 16); + ARL_CTRL0_REG |= (entry->pmap << 9); + + ARL_CTRL1_REG |= (entry->mac[0] << 24); + ARL_CTRL1_REG |= (entry->mac[1] << 16); + ARL_CTRL1_REG |= (entry->mac[2] << 8); + ARL_CTRL1_REG |= entry->mac[3]; + + ARL_CTRL2_REG |= (entry->mac[4] << 24); + ARL_CTRL2_REG |= (entry->mac[5] << 16); + + ARL_CTRL2_REG |= (entry->age_field << 4); + ARL_CTRL2_REG |= (entry->vlan_mac << 1); + ARL_CTRL2_REG |= (entry->filter); + + //printk("entry->age_field: %d\n", entry->age_field); + //printk("ARL_CTRL2_REG: %x\n", ARL_CTRL2_REG); + + ARL_VLAN_CMD_REG |= (1 << 19); // arl table write command + + // wait arl command complete + while(( (ARL_VLAN_CMD_REG >> 21) & 1) == 0); + + return CAVM_OK; +} + +// invalid a entry in the arl table +static inline int cns3xxx_arl_table_invalid(ARLTableEntry *entry) +{ + entry->age_field = 0; // invalid + return cns3xxx_arl_table_add(entry); +} + +// port: +// 0 : mac port0 +// 1 : mac port1 +// 2 : mac port2 +// 3 : cpu port +static inline void cns3xxx_set_pvid(u8 port, u16 pvid) +{ + switch (port) + { + case 0: + { + MAC1_MAC0_PVID_REG &= (~0x0fff); + MAC1_MAC0_PVID_REG |= pvid; + break; + } + case 1: + { + MAC1_MAC0_PVID_REG &= (~(0x0fff << 16)); + MAC1_MAC0_PVID_REG |= (pvid << 16); + break; + } + case 2: + { + MAC2_CPU_PVID_REG &= (~(0x0fff << 16)); + MAC2_CPU_PVID_REG |= (pvid << 16); + break; + } + case 3: // cpu port + { + MAC2_CPU_PVID_REG &= (~0x0fff); + MAC2_CPU_PVID_REG |= pvid; + break; + } + } + + +} + +static inline u16 cns3xxx_get_pvid(u8 port) +{ + // 0, 1, 2, cpu port + u16 port_offset[]={0x9c, 0x9c, 0xa0, 0xa0}; + // 0, 1, 2, cpu port + u16 port_shift[]={0, 16, 16, 0}; + + return ((SWITCH_REG_VALUE(port_offset[port]) >> port_shift[port]) & 0xfff); +} + +// which : 0 or 1 +// enable: 0 or 1 +static inline int enable_rx_dma(u8 which, u8 enable) +{ + if (which == 0 ) { + FS_DMA_CTRL0_REG = enable; + } else if (which == 1 ) { + FS_DMA_CTRL1_REG = enable; + } else { + return CAVM_ERR; + } + return CAVM_OK; +} + + +// which : 0 or 1 +// enable: 0 or 1 +static inline int enable_tx_dma(u8 which, u8 enable) +{ + if (which == 0 ) { + TS_DMA_CTRL0_REG = enable; + } else if (which == 1 ) { + TS_DMA_CTRL1_REG = enable; + } else { + return CAVM_ERR; + } + return CAVM_OK; +} + +#define DUMP_TX_DESC_PROC(tx_desc, page, num) \ +{ \ + num += sprintf(page + num,"sdp: %x\n", tx_desc->sdp); \ + num += sprintf(page + num,"sdl: %d\n", tx_desc->sdl); \ + num += sprintf(page + num,"tco: %d\n", tx_desc->tco); \ + num += sprintf(page + num,"uco: %d\n", tx_desc->uco); \ + num += sprintf(page + num,"ico: %d\n", tx_desc->ico); \ + num += sprintf(page + num,"pri: %d\n", tx_desc->pri); \ + num += sprintf(page + num,"fp: %d\n", tx_desc->fp); \ + num += sprintf(page + num,"fr: %d\n", tx_desc->fr); \ + num += sprintf(page + num,"interrupt: %d\n", tx_desc->interrupt); \ + num += sprintf(page + num,"lsd: %d\n", tx_desc->lsd); \ + num += sprintf(page + num,"fsd: %d\n", tx_desc->fsd); \ + num += sprintf(page + num,"eor: %d\n", tx_desc->eor); \ + num += sprintf(page + num,"cown: %d\n", tx_desc->cown); \ + \ + num += sprintf(page + num,"ctv: %d\n", tx_desc->ctv); \ + num += sprintf(page + num,"stv: %d\n", tx_desc->stv); \ + num += sprintf(page + num,"sid: %d\n", tx_desc->sid); \ + num += sprintf(page + num,"inss: %d\n", tx_desc->inss); \ + num += sprintf(page + num,"dels: %d\n", tx_desc->dels); \ + num += sprintf(page + num,"pmap: %d\n", tx_desc->pmap); \ + num += sprintf(page + num,"mark: %d\n", tx_desc->mark); \ + num += sprintf(page + num,"ewan: %d\n", tx_desc->ewan); \ + num += sprintf(page + num,"fewan: %d\n", tx_desc->fewan); \ + \ + num += sprintf(page + num,"c_vid: %d\n", tx_desc->c_vid); \ + num += sprintf(page + num,"c_cfs: %d\n", tx_desc->c_cfs); \ + num += sprintf(page + num,"c_pri: %d\n", tx_desc->c_pri); \ + num += sprintf(page + num,"s_vid: %d\n", tx_desc->s_vid); \ + num += sprintf(page + num,"s_dei: %d\n", tx_desc->s_dei); \ + num += sprintf(page + num,"s_pri: %d\n", tx_desc->s_pri); \ +} + +static inline void dump_tx_desc(TXDesc volatile *tx_desc) +{ + printk("sdp: %x\n", tx_desc->sdp); + printk("sdl: %d\n", tx_desc->sdl); + printk("tco: %d\n", tx_desc->tco); + printk("uco: %d\n", tx_desc->uco); + printk("ico: %d\n", tx_desc->ico); + printk("pri: %d\n", tx_desc->pri); + printk("fp: %d\n", tx_desc->fp); + printk("fr: %d\n", tx_desc->fr); + printk("interrupt: %d\n", tx_desc->interrupt); + printk("lsd: %d\n", tx_desc->lsd); + printk("fsd: %d\n", tx_desc->fsd); + printk("eor: %d\n", tx_desc->eor); + printk("cown: %d\n", tx_desc->cown); + + printk("ctv: %d\n", tx_desc->ctv); + printk("stv: %d\n", tx_desc->stv); + printk("sid: %d\n", tx_desc->sid); + printk("inss: %d\n", tx_desc->inss); + printk("dels: %d\n", tx_desc->dels); + printk("pmap: %d\n", tx_desc->pmap); + printk("mark: %d\n", tx_desc->mark); + printk("ewan: %d\n", tx_desc->ewan); + printk("fewan: %d\n", tx_desc->fewan); + + printk("c_vid: %d\n", tx_desc->c_vid); + printk("c_cfs: %d\n", tx_desc->c_cfs); + printk("c_pri: %d\n", tx_desc->c_pri); + printk("s_vid: %d\n", tx_desc->s_vid); + printk("s_dei: %d\n", tx_desc->s_dei); + printk("s_pri: %d\n", tx_desc->s_pri); +} + +static inline void dump_rx_desc(RXDesc volatile *rx_desc) +{ + + printk("rx_desc: %p\n", rx_desc); + //printk("rx_desc: %p ## cown: %d\n", rx_desc, rx_desc->cown); + //printk("rx_desc phy addr : %x\n", (u32)page_to_dma(NULL, rx_desc) ); +#if 0 + int i=0; + for (i=0; i < 8 ; ++4) { + u32 rx_desc_data = *((u32 *)(rx_desc+i)); + printk("%d: %#x\n", i, rx_desc_data); + } +#endif + + printk("sdp: %x\n", rx_desc->sdp); + + printk("sdl: %d\n", rx_desc->sdl); +#if 1 + printk("l4f: %d\n", rx_desc->l4f); + printk("ipf: %d\n", rx_desc->ipf); + printk("prot: %d\n", rx_desc->prot); + printk("hr: %d\n", rx_desc->hr); + printk("lsd: %d\n", rx_desc->lsd); + printk("fsd: %d\n", rx_desc->fsd); + printk("eor: %d\n", rx_desc->eor); +#endif + printk("cown: %d\n", rx_desc->cown); + +#if 1 + printk("ctv: %d\n", rx_desc->ctv); + printk("stv: %d\n", rx_desc->stv); + printk("unv: %d\n", rx_desc->unv); + printk("iwan: %d\n", rx_desc->iwan); + printk("exdv: %d\n", rx_desc->exdv); + printk("sp: %d\n", rx_desc->sp); + printk("crc_err: %d\n", rx_desc->crc_err); + printk("un_eth: %d\n", rx_desc->un_eth); + printk("tc: %d\n", rx_desc->tc); + printk("ip_offset: %d\n", rx_desc->ip_offset); + + printk("c_vid: %d\n", rx_desc->c_vid); + printk("c_cfs: %d\n", rx_desc->c_cfs); + printk("c_pri: %d\n", rx_desc->c_pri); + printk("s_vid: %d\n", rx_desc->s_vid); + printk("s_dei: %d\n", rx_desc->s_dei); + printk("s_pri: %d\n", rx_desc->s_pri); +#endif +} + +static inline void dump_all_rx_ring(const RXRing *rx_ring, u8 r_index) +{ + int i=0; + + RXBuffer volatile *rx_buf = get_rx_ring_head(rx_ring); + + printk("all rx ring: %d\n", r_index); + for (i=0 ; i < get_rx_ring_size(rx_ring) ; ++i) { + printk("%d ## rx_buf: %p ## rx_buf->rx_desc: %p\n", i, rx_buf, rx_buf->rx_desc); + dump_rx_desc(rx_buf->rx_desc); + ++rx_buf; + } +} + +static inline void rx_dma_suspend(u8 enable) +{ +#if 1 + DMA_AUTO_POLL_CFG_REG &= (~0x00000001); + if (enable == 1) + DMA_AUTO_POLL_CFG_REG |= 1; +#endif +} + + +// clear: 0 normal +// clear: 1 clear +static inline void clear_fs_dma_state(u8 clear) +{ + DMA_RING_CTRL_REG &= (~(1 << 31)); + if (clear==1) { + DMA_RING_CTRL_REG |= (1 << 31); + } +} + +// enable: 1 -> IVL +// enable: 0 -> SVL +static inline void cns3xxx_ivl(u8 enable) +{ + // SVL + MAC_GLOB_CFG_REG &= (~(0x1 << 7)); + if (enable == 1) + MAC_GLOB_CFG_REG |= (0x1 << 7); +} + +static inline void cns3xxx_nic_mode(u8 enable) +{ + VLAN_CFG &= (~(1<<15)); + if (enable == 1) + VLAN_CFG |= (1<<15); +} + + +void gic_mask_irq(unsigned int irq); +void gic_unmask_irq(unsigned int irq); +extern void __iomem *gic_cpu_base_addr; + + +static inline void cns3xxx_disable_irq(u32 irq) +{ +#ifdef CONFIG_SMP + disable_irq_nosync(irq); +#else + disable_irq(irq); +#endif + //gic_mask_irq(irq); +} + +static inline void cns3xxx_enable_irq(u32 irq) +{ + enable_irq(irq); + //gic_unmask_irq(irq); +} + +static inline int cns3xxx_get_tx_hw_index(u8 ring_index) +{ + if (ring_index == 0) { + return (TS_DESC_PTR0_REG - TS_DESC_BASE_ADDR0_REG) / sizeof (TXDesc); + } else if (ring_index == 1) { + return (TS_DESC_PTR1_REG - TS_DESC_BASE_ADDR1_REG) / sizeof (TXDesc); + } else { + return CAVM_ERR; + } +} + +static inline TXBuffer* get_tx_buffer_by_index(TXRing *tx_ring, int i) +{ + int index = i; + + index = ((index + get_tx_ring_size(tx_ring) )% get_tx_ring_size(tx_ring)); + + return tx_ring->head + index; +} + +static inline int cns3xxx_is_untag_packet(const RXDesc *rx_desc) +{ + return rx_desc->crc_err; +} + + +#ifdef CONFIG_SWITCH_BIG_ENDIAN +static inline void swap_rx_desc(RXDesc *org_desc, RXDesc *new_desc) +{ + int i=0; + void *org_p = org_desc; + void *new_p = new_desc; + + for (i=0; i < 16 ; i+=4) { + u32 rx_desc_data = 0; + u32 swab_rx_desc_data = 0; + + rx_desc_data = *((volatile u32 *)(org_p+i)); + swab_rx_desc_data = ___swab32(rx_desc_data); + + *((volatile u32 *)(new_p+i)) = swab_rx_desc_data; + } +} + +static inline void swap_tx_desc(TXDesc *org_desc, TXDesc *new_desc) +{ + int i=0; + void *org_p = org_desc; + void *new_p = new_desc; + + for (i=0; i < 16 ; i+=4) { + u32 desc_data = *((volatile u32 *)(org_p+i)); + u32 swab_desc_data = ___swab32(desc_data); + + *((volatile u32 *)(new_p+i)) = swab_desc_data; + } +} + +#endif + + +static inline int cns3xxx_min_mtu(void) +{ + return 64; +} + +static inline int cns3xxx_max_mtu(void) +{ + int max_len[]={1518, 1522, 1536, 9600}; + + return max_len[((PHY_AUTO_ADDR_REG >> 30) & 0x3)]; +} + +#endif // CNS3XXX_TOOL_H --- /dev/null +++ b/drivers/net/cns3xxx/fpga.h @@ -0,0 +1,306 @@ +/******************************************************************************* + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +// This macro or function divide two part, +// one is initial state, another is in netdev open (ifconfig up) function. + +#ifndef FPGA_H +#define FPGA_H + +#include + +#include "cns3xxx_config.h" +#include "cns3xxx_phy.h" + +//#define FGPA + + +#ifdef CONFIG_FPGA +// init phy or switch chip +#define INIT_PORT0_PHY cns3xxx_config_VSC8601(0,0); +#define INIT_PORT1_PHY cns3xxx_config_VSC8601(1,1); +#define INIT_PORT2_PHY icp_101a_init(2, 2); +//#define INIT_PORT1_PHY + +// configure mac0/mac1 register +#define INIT_PORT0_MAC +#define INIT_PORT1_MAC +#define INIT_PORT2_MAC +//#define INIT_PORT1_MAC + +#define PORT0_LINK_DOWN vsc8601_power_down(0, 1); +#define PORT0_LINK_UP vsc8601_power_down(0, 0); + +#define PORT1_LINK_DOWN vsc8601_power_down(1, 1); +#define PORT1_LINK_UP vsc8601_power_down(1, 0); + +#define PORT2_LINK_DOWN cns3xxx_std_phy_power_down(2, 1); +#define PORT2_LINK_UP cns3xxx_std_phy_power_down(2, 0); + + + +#define MODEL "VEGA FPGA" + +static int rc_port0 = 0; // rc means reference counting, determine port open/close. + + +// enable port +// link down +static inline void open_port0(void) +{ + if (rc_port0 == 0) { + enable_port(0, 1); + PRINT_INFO("open mac port 0\n"); + // link up + PORT0_LINK_UP + } else { + PRINT_INFO("port 0 already open\n");\ + } + ++rc_port0; +} + +static inline void close_port0(void) +{ + --rc_port0; + if (rc_port0 == 0) { + // link down + PORT0_LINK_DOWN + enable_port(0, 0); + PRINT_INFO("close mac port 0\n");\ + } +} + +static inline void open_port1(void) +{ + + enable_port(1, 1); + PRINT_INFO("open mac port 1\n"); + // link up + PORT1_LINK_UP +} + +static inline void close_port1(void) +{ + enable_port(1, 0); + PRINT_INFO("close mac port 1\n"); + // link down + PORT1_LINK_DOWN +} + +static inline void open_port2(void) +{ + + enable_port(2, 1); + PRINT_INFO("open mac port 2\n"); + // link up + PORT2_LINK_UP +} + +static inline void close_port2(void) +{ + enable_port(2, 0); + PRINT_INFO("close mac port 2\n"); + // link down + PORT2_LINK_DOWN +} + +static u8 my_vlan0_mac[] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x00}; +static u8 my_vlan1_mac[] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x11}; +static u8 my_vlan2_mac[] = {0x00, 0x11, 0xbb, 0xcc, 0xdd, 0x70}; +static u8 my_vlan3_mac[] = {0x00, 0x11, 0xbb, 0xcc, 0xdd, 0x80}; + + + + +// CNS3XXX_NIC_MODE_8021Q, CNS3XXX_NON_NIC_MODE_8021Q, CNS3XXX_VLAN_BASE_MODE and +// CNS3XXX_PORT_BASE_MODE, only one macro can be defined + +#ifdef CNS3XXX_VLAN_8021Q + #ifndef CNS3XXX_NIC_MODE_8021Q + #define CNS3XXX_NON_NIC_MODE_8021Q + #endif +#else + //#define CNS3XXX_VLAN_BASE_MODE + #define CNS3XXX_PORT_BASE_MODE +#endif + +#ifdef CNS3XXX_PORT_BASE_MODE + +#define PORT0_PVID 0x1 +#define PORT1_PVID 0x2 +#define PORT2_PVID 3 +#define CPU_PVID 5 + +#define CONFIG_CNS3XXX_PORT_BASE + +static VLANTableEntry cpu_vlan_table_entry = {0, 1, CPU_PVID, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}; + +static VLANTableEntry vlan_table_entry[] = +{ + // vlan_index; valid; vid; wan_side; etag_pmap; mb_pmap; *my_mac; + //{0, 1, 1, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {1, 1, PORT0_PVID, 0, 0, MAC_PORT0_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {2, 1, PORT1_PVID, 0, 0, MAC_PORT1_PMAP | CPU_PORT_PMAP, my_vlan1_mac}, + {3, 1, PORT2_PVID, 1, 0, MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan2_mac}, + //{2, 1, 4, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}, // for cpu +}; + +static ARLTableEntry arl_table_entry[] = +{ + // vid; pmap; *mac; age_field; vlan_mac ; filter + {PORT0_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + //{CPU_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + {PORT1_PVID, CPU_PORT_PMAP, my_vlan1_mac, 7, 1, 0}, + {PORT2_PVID, CPU_PORT_PMAP, my_vlan2_mac, 7, 1, 0}, + //{PORT0_PVID, MAC_PORT0_PMAP, my_vlan8_mac, 7, 0, 0}, + //{PORT0_PVID, MAC_PORT0_PMAP, my_vlan9_mac, 7, 0, 0}, + //{CPU_PVID, 0x4, my_vlan2_mac, 7, 1, 0}, + //{CPU_PVID, MAC_PORT2_PMAP, my_vlan2_mac, 7, 1, 0}, +}; + +static NetDevicePriv net_device_prive[]= { + /* pmap, is_wan, s-tag, vlan_tag or pvid, rx_func_ptr, tx_func_ptr, open_ptr, close_ptr, which port, mac, VLANTableEntry, ARLTableEntry, NICSetting, netdev s-tag, name */ + {MAC_PORT0_PMAP, 0, 1, PORT0_NETDEV_INDEX, rx_port_base, tx_port_base, open_port0, close_port0, MAC_PORT0, my_vlan0_mac, &vlan_table_entry[0], &arl_table_entry[0], 0, 0}, // eth0 + {MAC_PORT1_PMAP, 0, 2, PORT1_NETDEV_INDEX, rx_port_base, tx_port_base, open_port1, close_port1, MAC_PORT1, my_vlan1_mac, &vlan_table_entry[1], &arl_table_entry[1], 0, 0}, // eth1 + {MAC_PORT2_PMAP, 1, 3, PORT2_NETDEV_INDEX, rx_port_base, tx_port_base, open_port2, close_port2, MAC_PORT2, my_vlan2_mac, &vlan_table_entry[2], &arl_table_entry[2], 0, 0} // eth2 + }; + +#endif + +#ifdef CNS3XXX_NON_NIC_MODE_8021Q +//#error "8021Q" +#define PORT0_PVID 50 +#define PORT1_PVID 60 +#define PORT2_PVID 70 +#define CPU_PVID 80 + +#define CONFIG_CNS3XXX_PORT_BASE +//#define CONFIG_CNS3XXX_VLAN_BASE +//#define CONFIG_HAVE_VLAN_TAG + +static VLANTableEntry cpu_vlan_table_entry = {0, 1, CPU_PVID, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}; // for cpu + +static VLANTableEntry vlan_table_entry[] = +{ + // vlan_index; valid; vid; wan_side; etag_pmap; mb_pmap; *my_mac;C_PORT2_PMAP + {1, 1, PORT0_PVID, 0, CPU_PORT_PMAP, MAC_PORT0_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {2, 1, PORT1_PVID, 0, CPU_PORT_PMAP, MAC_PORT1_PMAP | CPU_PORT_PMAP, my_vlan1_mac}, + {3, 1, PORT2_PVID, 0, CPU_PORT_PMAP, MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan2_mac}, +}; + +static ARLTableEntry arl_table_entry[] = +{ + // vid; pmap; *mac; age_field; vlan_mac ; filter + {PORT0_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + {PORT1_PVID, CPU_PORT_PMAP, my_vlan1_mac, 7, 1, 0}, + {PORT2_PVID, CPU_PORT_PMAP, my_vlan2_mac, 7, 1, 0}, +}; + + +// if used 8021Q, use PORT0_NETDEV_INDEX, don't use VID +static NetDevicePriv net_device_prive[]= { + {MAC_PORT0_PMAP, 0, 1, PORT0_NETDEV_INDEX, rx_port_base, tx_port_base, open_port0, close_port0, MAC_PORT0, my_vlan0_mac, &vlan_table_entry[0], &arl_table_entry[0], 0, 0}, // eth0 + {MAC_PORT1_PMAP, 0, 0, PORT1_NETDEV_INDEX, rx_port_base, tx_port_base, open_port1, close_port1, MAC_PORT1, my_vlan1_mac, &vlan_table_entry[1], &arl_table_entry[1], 0, 0}, // eth1 + {MAC_PORT2_PMAP, 1, 3, PORT2_NETDEV_INDEX, rx_port_base, tx_port_base, open_port2, close_port2, MAC_PORT2, my_vlan2_mac, &vlan_table_entry[2], &arl_table_entry[2], 0, 0} // eth2 + }; +#endif + + + +#ifdef CNS3XXX_NIC_MODE_8021Q +//#error "8021Q" +#define PORT0_PVID 1 +#define PORT1_PVID 2 +#define PORT2_PVID 9 +#define CPU_PVID 5 + +#define CONFIG_CNS3XXX_PORT_BASE +//#define CONFIG_CNS3XXX_VLAN_BASE +//#define CONFIG_HAVE_VLAN_TAG + +static VLANTableEntry cpu_vlan_table_entry = {0, 1, CPU_PVID, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}; // for cpu + +static VLANTableEntry vlan_table_entry[] = +{ + // vlan_index; valid; vid; wan_side; etag_pmap; mb_pmap; *my_mac;C_PORT2_PMAP + {1, 1, PORT0_PVID, 1, MAC_PORT0_PMAP|CPU_PORT_PMAP, MAC_PORT0_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {2, 1, PORT1_PVID, 0, MAC_PORT1_PMAP|CPU_PORT_PMAP, MAC_PORT1_PMAP | CPU_PORT_PMAP, my_vlan1_mac}, + {3, 1, PORT2_PVID, 1, MAC_PORT2_PMAP|CPU_PORT_PMAP, MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan2_mac}, +}; + +static ARLTableEntry arl_table_entry[] = +{ + // vid; pmap; *mac; age_field; vlan_mac ; filter + {PORT0_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + {PORT1_PVID, CPU_PORT_PMAP, my_vlan1_mac, 7, 1, 0}, + {PORT2_PVID, CPU_PORT_PMAP, my_vlan2_mac, 7, 1, 0}, +}; + + +// if used 8021Q, use PORT0_NETDEV_INDEX, don't use VID +static NetDevicePriv net_device_prive[]= { + {MAC_PORT0_PMAP, 0, 1, PORT0_NETDEV_INDEX, rx_port_base, tx_port_base, open_port0, close_port0, MAC_PORT0, my_vlan0_mac, &vlan_table_entry[0], &arl_table_entry[0], 0, 0}, // eth0 + {MAC_PORT1_PMAP, 0, 0, PORT1_NETDEV_INDEX, rx_port_base, tx_port_base, open_port1, close_port1, MAC_PORT1, my_vlan1_mac, &vlan_table_entry[1], &arl_table_entry[1], 0, 0}, // eth1 + {MAC_PORT2_PMAP, 1, 3, PORT2_NETDEV_INDEX, rx_port_base, tx_port_base, open_port2, close_port2, MAC_PORT2, my_vlan2_mac, &vlan_table_entry[2], &arl_table_entry[2], 0, 0} // eth2 + }; +#endif + +#ifdef CNS3XXX_VLAN_BASE_MODE +//#error "vlan_base" +// vlan configuration + +#define PORT0_PVID 1 +#define PORT1_PVID 2 +#define PORT2_PVID 3 +#define CPU_PVID 5 +#define CONFIG_CNS3XXX_VLAN_BASE +#define CONFIG_HAVE_VLAN_TAG + +static VLANTableEntry cpu_vlan_table_entry = {0, 1, CPU_PVID, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}; // for cpu + +static VLANTableEntry vlan_table_entry[] = +{ + // vlan_index; valid; vid; wan_side; etag_pmap; mb_pmap; *my_mac; + {1, 1, PORT0_PVID, 0, MAC_PORT0_PMAP | CPU_PORT_PMAP, MAC_PORT0_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {2, 1, PORT1_PVID, 0, MAC_PORT1_PMAP | CPU_PORT_PMAP, MAC_PORT1_PMAP | CPU_PORT_PMAP, my_vlan1_mac}, + {3, 1, PORT2_PVID, 1, MAC_PORT2_PMAP | CPU_PORT_PMAP, MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan2_mac}, +}; + +static ARLTableEntry arl_table_entry[] = +{ + // vid; pmap; *mac; age_field; vlan_mac ; filter + {PORT0_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + {PORT1_PVID, CPU_PORT_PMAP, my_vlan1_mac, 7, 1, 0}, + {PORT2_PVID, CPU_PORT_PMAP, my_vlan2_mac, 7, 1, 0}, +}; + +static NetDevicePriv net_device_prive[]= { + /* pmap, is_wan, gid, vlan_tag or pvid, rx_func_ptr, tx_func_ptr, open_ptr, close_ptr, which port, mac, VLANTableEntry, ARLTableEntry, NICSetting, netdev name */ + {MAC_PORT0_PMAP, 0, 1, PORT0_PVID, rx_port_base, tx_vlan_base, open_port0, close_port0, MAC_PORT0, my_vlan0_mac, &vlan_table_entry[0], &arl_table_entry[0], 0, 0}, // eth0 + {MAC_PORT1_PMAP, 0, 0, PORT1_PVID, rx_port_base, tx_vlan_base, open_port1, close_port1, MAC_PORT1, my_vlan1_mac, &vlan_table_entry[1], &arl_table_entry[1], 0, 0}, // eth1 + {MAC_PORT2_PMAP, 1, 3, PORT2_PVID, rx_port_base, tx_vlan_base, open_port2, close_port2, MAC_PORT2, my_vlan2_mac, &vlan_table_entry[2], &arl_table_entry[2], 0, 0} // eth2 + }; +#endif + +#endif // CONFIG_FPGA +#endif // FPGA_H --- /dev/null +++ b/drivers/net/cns3xxx/Kconfig @@ -0,0 +1,58 @@ +menu "CNS3XXX Gigabit Switch Support" + depends on ARCH_CNS3XXX + +config CNS3XXX_GSW + tristate "CNS3XXX Gigabit Switch Driver Support" + help + CNS3XXX Gigabit Switch. + +config CNS3XXX_SPPE + bool "CNS3XXX Smart PPE(Packet Processing Engine) Support" + depends on CNS3XXX_GSW + help + PPE(Packet Processing Engine) is a hardware accelerator hook on a port of + CNS3XXX Gigabit Switch. + + This option is used for Smart PPE hook. + + Say Y if you want to enable Smart PPE function. + +config CNS3XXX_HCIE_TEST + bool "CNS3XXX HCIE(Hardware Content Inspection Engine) Support" +# depends on CNS3XXX_GSW + help + HCIE is patent-protected layer-7 packet processing engine. + + This option is used for fundamental HCIE functional test . + Say Y if you want to do HCIE functional test. + + +#config CNS3XXX_SHNAT_PCI_FASTPATH +# bool "FastPath(From PCI to WAN) Support" +# depends on CNS3XXX_SHNAT +# help +# Add FastPath Support for Smart HNAT. + +comment "NOTE: 'Validation Board' depends on" +comment "GPIO_CNS3XXX and SPI_CNS3XXX" +choice + prompt "CNS3XXX Board" + depends on CNS3XXX_GSW + default FPGA + +config FPGA + bool "Fpga" + +config VB + bool "Validation Board" + help + MAC0 and MAC1 connect to BCM53115M. It need enable CNS3XXX SPI and CNS3XXX GPIO option. + MAC2 use ICPLUS IP1001 phy. + +#config LEO +# bool "Leo" + +endchoice + +endmenu + --- /dev/null +++ b/drivers/net/cns3xxx/Makefile @@ -0,0 +1,41 @@ +################################################################################ +# +# +# Copyright (c) 2008 Cavium Networks +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# The full GNU General Public License is included in this distribution in the +# file called LICENSE. +# +# Contact Information: +# Star semiconduction Linux Support +# +################################################################################ + +# +# Makefile for the Star GSW ethernet driver +# + +#obj-y := +#obj-m := + +obj-$(CONFIG_CNS3XXX_GSW) += cns3xxx.o +cns3xxx-objs := cns3xxx_phy.o cns3xxx_main.o cns3xxx_ethtool.o +obj-$(CONFIG_CNS3XXX_SPPE) += cns3xxx_sppe_hook.o +#endif +#vega_main.o + +#include $(TOPDIR)/Rules.make --- /dev/null +++ b/drivers/net/cns3xxx/vb.h @@ -0,0 +1,328 @@ +/******************************************************************************* + * + * Copyright (c) 2009 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + ********************************************************************************/ + +// This macro or function divide two part, +// one is initial state, another is in netdev open (ifconfig up) function. + +#ifndef VB_H +#define VB_H + +#include + +#include "cns3xxx_config.h" +#include "cns3xxx_phy.h" + +#ifdef CONFIG_VB +// init phy or switch chip +#define INIT_PORT0_PHY cns3xxx_config_VSC8601(0, 0); +#define INIT_PORT1_PHY cns3xxx_config_VSC8601(1, 1); +#define INIT_PORT2_PHY +//#define INIT_PORT1_PHY + +// configure mac0/mac1 register +#define INIT_PORT0_MAC +#define INIT_PORT1_MAC +#define INIT_PORT2_MAC +//#define INIT_PORT1_MAC + +#define PORT0_LINK_DOWN cns3xxx_std_phy_power_down(0, 1); +#define PORT0_LINK_UP cns3xxx_std_phy_power_down(0, 0); + +#define PORT1_LINK_DOWN cns3xxx_std_phy_power_down(1, 1); +#define PORT1_LINK_UP cns3xxx_std_phy_power_down(1, 0); + +#define PORT2_LINK_DOWN +#define PORT2_LINK_UP + +#define MODEL "CNS3XXX validation board" + +static int rc_port0 = 0; // rc means reference counting, determine port open/close. + +#define PRINT_INFO printk + +// enable port +// link down +static inline void open_port0(void) +{ + if (rc_port0 == 0) { + enable_port(0, 1); + // link up + PORT0_LINK_UP + } + ++rc_port0; +} + +static inline void close_port0(void) +{ + --rc_port0; + if (rc_port0 == 0) { + // link down + PORT0_LINK_DOWN + enable_port(0, 0); + } +} + +static inline void open_port1(void) +{ + + enable_port(1, 1); + // link up + PORT1_LINK_UP +} + +static inline void close_port1(void) +{ + enable_port(1, 0); + // link down + PORT1_LINK_DOWN +} + +static inline void open_port2(void) +{ + + enable_port(2, 1); + // link up + PORT2_LINK_UP +} + +static inline void close_port2(void) +{ + enable_port(2, 0); + // link down + PORT2_LINK_DOWN +} + +#if defined (CONFIG_CNS3XXX_SPPE) +/* only for PPE PCI-to-WAN fast path */ +static int fp_ref_cnt = 0; +static inline void open_fp(void) +{ + if (!fp_ref_cnt) { + fp_ref_cnt++; + } +} + +static inline void close_fp(void) +{ + if (fp_ref_cnt) { + fp_ref_cnt--; + } +} +#endif + +static u8 my_vlan0_mac[] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x00}; +static u8 my_vlan1_mac[] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x11}; +static u8 my_vlan2_mac[] = {0x00, 0x11, 0xbb, 0xcc, 0xdd, 0x70}; +static u8 my_vlan3_mac[] = {0x00, 0x11, 0xbb, 0xcc, 0xdd, 0x80}; + + + + +// CNS3XXX_NIC_MODE_8021Q, CNS3XXX_NON_NIC_MODE_8021Q, CNS3XXX_VLAN_BASE_MODE and +// CNS3XXX_PORT_BASE_MODE, only one macro can be defined + +#ifdef CNS3XXX_VLAN_8021Q + #define CNS3XXX_NIC_MODE_8021Q + #ifndef CNS3XXX_NIC_MODE_8021Q + #define CNS3XXX_NON_NIC_MODE_8021Q + #endif +#else + //#define CNS3XXX_VLAN_BASE_MODE + #define CNS3XXX_PORT_BASE_MODE +#endif + +//#define CNS3XXX_PORT_BASE_MODE +// +#ifdef CNS3XXX_NON_NIC_MODE_8021Q + +#define PORT0_PVID 50 +#define PORT1_PVID 60 +#define PORT2_PVID 70 +#define CPU_PVID 80 + +#define CONFIG_CNS3XXX_PORT_BASE + +static VLANTableEntry cpu_vlan_table_entry = {0, 1, CPU_PVID, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}; + +static VLANTableEntry vlan_table_entry[] = +{ + // vlan_index; valid; vid; wan_side; etag_pmap; mb_pmap; *my_mac; + #if 0 + {1, 1, PORT0_PVID, 0, 0, MAC_PORT0_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {2, 1, PORT1_PVID, 0, 0, MAC_PORT1_PMAP | CPU_PORT_PMAP, my_vlan1_mac}, + {3, 1, PORT2_PVID, 1, 0, MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan2_mac}, + #endif + + {1, 1, PORT0_PVID, 0, CPU_PORT_PMAP, MAC_PORT0_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {2, 1, PORT1_PVID, 0, CPU_PORT_PMAP, MAC_PORT1_PMAP | CPU_PORT_PMAP, my_vlan1_mac}, + {3, 1, PORT2_PVID, 0, CPU_PORT_PMAP, MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan2_mac}, + //{2, 1, 4, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}, // for cpu +}; + +static ARLTableEntry arl_table_entry[] = +{ + // vid; pmap; *mac; age_field; vlan_mac ; filter + {PORT0_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + //{CPU_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + {PORT1_PVID, CPU_PORT_PMAP, my_vlan1_mac, 7, 1, 0}, + {PORT2_PVID, CPU_PORT_PMAP, my_vlan2_mac, 7, 1, 0}, + //{PORT0_PVID, MAC_PORT0_PMAP, my_vlan8_mac, 7, 0, 0}, + //{PORT0_PVID, MAC_PORT0_PMAP, my_vlan9_mac, 7, 0, 0}, + //{CPU_PVID, 0x4, my_vlan2_mac, 7, 1, 0}, + //{CPU_PVID, MAC_PORT2_PMAP, my_vlan2_mac, 7, 1, 0}, +}; + +static NetDevicePriv net_device_prive[]= { + /* pmap, is_wan, s-tag, vlan_tag or pvid, rx_func_ptr, tx_func_ptr, open_ptr, close_ptr, which port, mac, VLANTableEntry, ARLTableEntry, NICSetting, netdev s-tag, name */ + {MAC_PORT0_PMAP, 0, 1, PORT0_NETDEV_INDEX, rx_port_base, tx_port_base, open_port0, close_port0, MAC_PORT0, my_vlan0_mac, &vlan_table_entry[0], &arl_table_entry[0], 0, 0}, // eth0 + {MAC_PORT1_PMAP, 0, 2, PORT1_NETDEV_INDEX, rx_port_base, tx_port_base, open_port1, close_port1, MAC_PORT1, my_vlan1_mac, &vlan_table_entry[1], &arl_table_entry[1], 0, 0}, // eth1 + {MAC_PORT2_PMAP, 1, 3, PORT2_NETDEV_INDEX, rx_port_base, tx_port_base, open_port2, close_port2, MAC_PORT2, my_vlan2_mac, &vlan_table_entry[2], &arl_table_entry[2], 0, 0} // eth2 +#if defined (CONFIG_CNS3XXX_SPPE) + ,{CPU_PORT_PMAP, 0, 1, FP_NETDEV_INDEX, NULL, fp_port_base, + open_fp, close_fp, CPU_PORT, my_vlan3_mac, &cpu_vlan_table_entry, + 0, 0, "fp"} +#endif + }; + +#endif // CNS3XXX_PORT_BASE_MODE + +#ifdef CNS3XXX_PORT_BASE_MODE + +#define PORT0_PVID 0x1 +#define PORT1_PVID 0x2 +#define PORT2_PVID 3 +#define CPU_PVID 5 + +#define CONFIG_CNS3XXX_PORT_BASE + +static VLANTableEntry cpu_vlan_table_entry = {0, 1, CPU_PVID, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}; + +static VLANTableEntry vlan_table_entry[] = +{ + // vlan_index; valid; vid; wan_side; etag_pmap; mb_pmap; *my_mac; + //{0, 1, 1, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {1, 1, PORT0_PVID, 0, 0, MAC_PORT0_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {2, 1, PORT1_PVID, 0, 0, MAC_PORT1_PMAP | CPU_PORT_PMAP, my_vlan1_mac}, + {3, 1, PORT2_PVID, 1, 0, MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan2_mac}, + //{2, 1, 4, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}, // for cpu +}; + +static ARLTableEntry arl_table_entry[] = +{ + // vid; pmap; *mac; age_field; vlan_mac ; filter + {PORT0_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + //{CPU_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + {PORT1_PVID, CPU_PORT_PMAP, my_vlan1_mac, 7, 1, 0}, + {PORT2_PVID, CPU_PORT_PMAP, my_vlan2_mac, 7, 1, 0}, + //{PORT0_PVID, MAC_PORT0_PMAP, my_vlan8_mac, 7, 0, 0}, + //{PORT0_PVID, MAC_PORT0_PMAP, my_vlan9_mac, 7, 0, 0}, + //{CPU_PVID, 0x4, my_vlan2_mac, 7, 1, 0}, + //{CPU_PVID, MAC_PORT2_PMAP, my_vlan2_mac, 7, 1, 0}, +}; + +static NetDevicePriv net_device_prive[]= { + /* pmap, is_wan, s-tag, vlan_tag or pvid, rx_func_ptr, tx_func_ptr, open_ptr, close_ptr, which port, mac, VLANTableEntry, ARLTableEntry, NICSetting, netdev s-tag, name */ + {MAC_PORT0_PMAP, 0, 1, PORT0_NETDEV_INDEX, rx_port_base, tx_port_base, open_port0, close_port0, MAC_PORT0, my_vlan0_mac, &vlan_table_entry[0], &arl_table_entry[0], 0, 0}, // eth0 + {MAC_PORT1_PMAP, 0, 2, PORT1_NETDEV_INDEX, rx_port_base, tx_port_base, open_port1, close_port1, MAC_PORT1, my_vlan1_mac, &vlan_table_entry[1], &arl_table_entry[1], 0, 0}, // eth1 + {MAC_PORT2_PMAP, 1, 3, PORT2_NETDEV_INDEX, rx_port_base, tx_port_base, open_port2, close_port2, MAC_PORT2, my_vlan2_mac, &vlan_table_entry[2], &arl_table_entry[2], 0, 0} // eth2 + }; + +#endif // CNS3XXX_PORT_BASE_MODE + +#ifdef CNS3XXX_NIC_MODE_8021Q +//#error "8021Q" +#define PORT0_PVID 1 +#define PORT1_PVID 2 +#define PORT2_PVID 9 +#define CPU_PVID 5 + +#define CONFIG_CNS3XXX_PORT_BASE +//#define CONFIG_CNS3XXX_VLAN_BASE +//#define CONFIG_HAVE_VLAN_TAG + +static VLANTableEntry cpu_vlan_table_entry = {0, 1, CPU_PVID, 0, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}; // for cpu + +static VLANTableEntry vlan_table_entry[] = +{ + // vlan_index; valid; vid; wan_side; etag_pmap; mb_pmap; *my_mac;C_PORT2_PMAP + {1, 1, PORT0_PVID, 1, MAC_PORT0_PMAP|CPU_PORT_PMAP, MAC_PORT0_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {2, 1, PORT1_PVID, 0, MAC_PORT1_PMAP|CPU_PORT_PMAP, MAC_PORT1_PMAP | CPU_PORT_PMAP, my_vlan1_mac}, + {3, 1, PORT2_PVID, 1, MAC_PORT2_PMAP|CPU_PORT_PMAP, MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan2_mac}, +}; + +static ARLTableEntry arl_table_entry[] = +{ + // vid; pmap; *mac; age_field; vlan_mac ; filter + {PORT0_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + {PORT1_PVID, CPU_PORT_PMAP, my_vlan1_mac, 7, 1, 0}, + {PORT2_PVID, CPU_PORT_PMAP, my_vlan2_mac, 7, 1, 0}, +}; + + +// if used 8021Q, use PORT0_NETDEV_INDEX, don't use VID +static NetDevicePriv net_device_prive[]= { + {MAC_PORT0_PMAP, 0, 1, PORT0_NETDEV_INDEX, rx_port_base, tx_port_base, open_port0, close_port0, MAC_PORT0, my_vlan0_mac, &vlan_table_entry[0], &arl_table_entry[0], 0, 0}, // eth0 + {MAC_PORT1_PMAP, 0, 0, PORT1_NETDEV_INDEX, rx_port_base, tx_port_base, open_port1, close_port1, MAC_PORT1, my_vlan1_mac, &vlan_table_entry[1], &arl_table_entry[1], 0, 0}, // eth1 + {MAC_PORT2_PMAP, 1, 3, PORT2_NETDEV_INDEX, rx_port_base, tx_port_base, open_port2, close_port2, MAC_PORT2, my_vlan2_mac, &vlan_table_entry[2], &arl_table_entry[2], 0, 0} // eth2 + }; +#endif // CNS3XXX_NIC_MODE_8021Q + +#ifdef CNS3XXX_VLAN_BASE_MODE +//#error "vlan_base" +// vlan configuration + +#define PORT0_PVID 1 +#define PORT1_PVID 2 +#define PORT2_PVID 3 +#define CPU_PVID 5 +#define CONFIG_CNS3XXX_VLAN_BASE +#define CONFIG_HAVE_VLAN_TAG + +static VLANTableEntry cpu_vlan_table_entry = {0, 1, CPU_PVID, 0, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, MAC_PORT0_PMAP | MAC_PORT1_PMAP | MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan3_mac}; // for cpu + +static VLANTableEntry vlan_table_entry[] = +{ + // vlan_index; valid; vid; wan_side; etag_pmap; mb_pmap; *my_mac; + {1, 1, PORT0_PVID, 0, MAC_PORT0_PMAP | CPU_PORT_PMAP, MAC_PORT0_PMAP | CPU_PORT_PMAP, my_vlan0_mac}, + {2, 1, PORT1_PVID, 0, MAC_PORT1_PMAP | CPU_PORT_PMAP, MAC_PORT1_PMAP | CPU_PORT_PMAP, my_vlan1_mac}, + {3, 1, PORT2_PVID, 1, MAC_PORT2_PMAP | CPU_PORT_PMAP, MAC_PORT2_PMAP | CPU_PORT_PMAP, my_vlan2_mac}, +}; + +static ARLTableEntry arl_table_entry[] = +{ + // vid; pmap; *mac; age_field; vlan_mac ; filter + {PORT0_PVID, CPU_PORT_PMAP, my_vlan0_mac, 7, 1, 0}, + {PORT1_PVID, CPU_PORT_PMAP, my_vlan1_mac, 7, 1, 0}, + {PORT2_PVID, CPU_PORT_PMAP, my_vlan2_mac, 7, 1, 0}, +}; + +static NetDevicePriv net_device_prive[]= { + /* pmap, is_wan, gid, vlan_tag or pvid, rx_func_ptr, tx_func_ptr, open_ptr, close_ptr, which port, mac, VLANTableEntry, ARLTableEntry, NICSetting, netdev name */ + {MAC_PORT0_PMAP, 0, 1, PORT0_PVID, rx_port_base, tx_vlan_base, open_port0, close_port0, MAC_PORT0, my_vlan0_mac, &vlan_table_entry[0], &arl_table_entry[0], 0, 0}, // eth0 + {MAC_PORT1_PMAP, 0, 0, PORT1_PVID, rx_port_base, tx_vlan_base, open_port1, close_port1, MAC_PORT1, my_vlan1_mac, &vlan_table_entry[1], &arl_table_entry[1], 0, 0}, // eth1 + {MAC_PORT2_PMAP, 1, 3, PORT2_PVID, rx_port_base, tx_vlan_base, open_port2, close_port2, MAC_PORT2, my_vlan2_mac, &vlan_table_entry[2], &arl_table_entry[2], 0, 0} // eth2 + }; +#endif // CNS3XXX_VLAN_BASE_MODE + +#endif // CONFIG_VB +#endif // VB_H --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2076,6 +2076,8 @@ menuconfig NETDEV_1000 if NETDEV_1000 +source "drivers/net/cns3xxx/Kconfig" + config ACENIC tristate "Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support" depends on PCI --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -6,6 +6,11 @@ obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci obj-$(CONFIG_E1000) += e1000/ obj-$(CONFIG_E1000E) += e1000e/ +obj-$(CONFIG_CNS3XXX_GSW) += cns3xxx/ +ifeq ($(CONFIG_CNS3XXX_GSW),m) + obj-y += cns3xxx/cns3xxx_sppe_hook.o +endif + obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac/ obj-$(CONFIG_IGB) += igb/ obj-$(CONFIG_IGBVF) += igbvf/ --- /dev/null +++ b/include/linux/cns3xxx/sppe.h @@ -0,0 +1,1579 @@ +/* + * PROJECT CODE: CNS3XXX Smart Packet Processing Engine + * MODULE NAME: sppe.h + * DESCRIPTION: + * + * Change Log + * + * 1.0.0 25-Dec-2008 + * o + * + */ + +#ifndef _SPPE_H_ +#define _SPPE_H_ + +#if defined(CONFIG_CNS3XXX_SPPE) + + +/* PPE Table Size Def. */ +#define PPE_TABLE_SIZE_2K (0x0) +#define PPE_TABLE_SIZE_4K (0x1) +#define PPE_TABLE_SIZE_8K (0x2) +#define PPE_TABLE_SIZE_16K (0x3) +#define PPE_TABLE_SIZE_32K (0x4) +#define PPE_TABLE_SIZE_64K (0x5) +#define PPE_TABLE_SIZE_128K (0x6) +#define PPE_TABLE_SIZE_256K (0x7) + +typedef enum _sppe_cmd { + SPPE_CMD_INIT = 0, + SPPE_CMD_VERSION, + + SPPE_CMD_ENABLE, + SPPE_CMD_FIREWALL, + SPPE_CMD_RULE_CHECK, + SPPE_CMD_GRL_CHECK, + SPPE_CMD_FLOW_CHECK, + SPPE_CMD_RATE_LIMIT_EN, + SPPE_CMD_POLICE_EN, + SPPE_CMD_RLCFG, + SPPE_CMD_FC, /* flow control */ + SPPE_CMD_MIRROR_TO_CPU, + + SPPE_CMD_TCP_SNA_TH, + SPPE_CMD_PRDA, + SPPE_CMD_AGING, + SPPE_CMD_MAX_LENGTH, + + SPPE_CMD_LANIPV4, + SPPE_CMD_WANIPV4, + + SPPE_CMD_RULE_PPPOE_RELAY, + SPPE_CMD_RULE_BRIDGE, + SPPE_CMD_RULE_ACL, + SPPE_CMD_RULE_ROUTE, +#if 0 + SPPE_CMD_RULE_VSERVER, +#else + SPPE_CMD_RULE_SNAT, + SPPE_CMD_RULE_DNAT, +#endif + SPPE_CMD_RULE_GRL, + + SPPE_CMD_ARP, + SPPE_CMD_ARL, + SPPE_CMD_PPPOE_SID, + + SPPE_CMD_FLOW_BRIDGE_IPV4, + SPPE_CMD_FLOW_BRIDGE_IPV6, + SPPE_CMD_FLOW_ROUTE_IPV4, + SPPE_CMD_FLOW_ROUTE_IPV6, + SPPE_CMD_FLOW_NAT_IPV4, + SPPE_CMD_FLOW_NAT_IPV6, + //SPPE_CMD_FLOW_TWICE_NAT, + SPPE_CMD_FLOW_MCAST_IPV4, + SPPE_CMD_FLOW_MCAST_IPV6, + SPPE_CMD_FLOW_BRIDGE_L2, + + SPPE_CMD_CHGDSCP, + SPPE_CMD_CHGPRI, + SPPE_CMD_RL_FLOW, + SPPE_CMD_RL_RULE, + + SPPE_CMD_DEBUG, + SPPE_CMD_REG, + SPPE_CMD_SRAM, + SPPE_CMD_DUMP, + + /* accounting group and drop packet count */ + SPPE_CMD_ACCOUNTING_GROUP, + SPPE_CMD_DROP_IPCS_ERR, + SPPE_CMD_DROP_RATE_LIMIT, + SPPE_CMD_DROP_OTHERS, + + SPPE_CMD_PCI_FP_DEV, + +} SPPE_CMD; + +typedef enum _sppe_op { + SPPE_OP_GET = 0, + SPPE_OP_SET, + SPPE_OP_DELETE, + SPPE_OP_DELETE_OUTDATED, /* flow only */ + SPPE_OP_UPDATE_COUNTER, /* ACL rule only */ + SPPE_OP_CLEAN, + SPPE_OP_UNKNOWN +} SPPE_OP; + +typedef enum _sppe_boolean { + SPPE_BOOL_FALSE = 0, + SPPE_BOOL_TRUE = 1 +} SPPE_BOOL; + + +typedef enum _sppe_result { + SPPE_RESULT_SUCCESS = 0, + SPPE_RESULT_FAIL, + SPPE_RESULT_UNSUPPORT_CMD, + SPPE_RESULT_UNSUPPORT_OP, + SPPE_RESULT_INVALID_INDEX, + SPPE_RESULT_INVALID_TYPE, + SPPE_RESULT_FLOW_NOT_FOUND, +} SPPE_RESULT; + +typedef enum _sppe_prot { + SPPE_PROT_UDP = 0, + SPPE_PROT_TCP = 1, + SPPE_PROT_PPTP_GRE = 2, + SPPE_PROT_OTHERS = 3, +} SPPE_PROT; + + +typedef enum _sppe_l2_select { + SPPE_L2S_ARP_TABLE = 0, + SPPE_L2S_POLICY_ROUTE = 1, + SPPE_L2S_IN_FLOW = 2, + SPPE_L2S_RESERVED = 3, +} SPPE_L2_SELECT; + +typedef enum _sppe_dump_type { + SPPE_DUMP_TYPE_FLOW = 0, + SPPE_DUMP_TYPE_ARP, + SPPE_DUMP_TYPE_RULE +} SPPE_DUMP_TYPE; + +/* Data Structure */ +typedef struct _sppe_pppoe_relay { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int valid:1; + unsigned int unused:31; +#else + unsigned int unused:31; + unsigned int valid:1; +#endif + unsigned short lsid; /* PPPoE session ID in LAN side */ + unsigned short wsid; /* PPPoE session ID in WAN side */ + unsigned char lmac[6]; /* MAC address of PPPoE client */ + unsigned char wmac[6]; /* MAC address of PPPoE server */ +} SPPE_PPPOE_RELAY; + +typedef struct _sppe_bridge { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int valid:1; + unsigned int wan:1; + unsigned int ppp:1; /* enable PPPoE sessoion ID comparison*/ + unsigned int psidx:4; /* PPPoE session ID index */ + unsigned int kv:1; + unsigned int sws:1; + unsigned int max_len:2; /* Max. length select */ + unsigned int fp:1; /* force VLAN priority */ + unsigned int pri:3; + unsigned int ag:2; + unsigned int unused:15; +#else + unsigned int unused:15; + unsigned int ag:2; + unsigned int pri:3; + unsigned int fp:1; /* force VLAN priority */ + unsigned int max_len:2; /* Max. length select */ + unsigned int sws:1; + unsigned int kv:1; + unsigned int psidx:4; /* PPPoE session ID index */ + unsigned int ppp:1; /* enable PPPoE sessoion ID comparison*/ + unsigned int wan:1; + unsigned int valid:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int svid:12; + unsigned int cvid:12; + unsigned int loc:8; +#else + unsigned int loc:8; + unsigned int cvid:12; + unsigned int svid:12; +#endif + + unsigned char smac[6]; /* source MAC address */ + unsigned char dmac[6]; /* destination MAC address */ + unsigned int pkt_cnt; +} SPPE_BRIDGE; + +typedef struct _sppe_acl { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int valid:1; + unsigned int ipv6:1; + unsigned int wan:1; + unsigned int tcp:1; + unsigned int udp:1; + unsigned int to:4; + unsigned int from:4; + unsigned int rr:4; + unsigned int kv:1; + unsigned int sws:1; + unsigned int loc:8; + unsigned int max_len:2; /* Max. length select */ + unsigned int unused:3; +#else + unsigned int unused:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int loc:8; + unsigned int sws:1; + unsigned int kv:1; + unsigned int rr:4; + unsigned int from:4; + unsigned int to:4; + unsigned int udp:1; + unsigned int tcp:1; + unsigned int wan:1; + unsigned int ipv6:1; + unsigned int valid:1; +#endif + + unsigned int sip[4]; + unsigned int dip[4]; + unsigned short sip_mask; + unsigned short dip_mask; + + unsigned short sport_start; + unsigned short sport_end; + unsigned short dport_start; + unsigned short dport_end; + unsigned int pkt_cnt; +} SPPE_ACL; + +typedef struct _sppe_route { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int valid:1; + unsigned int ipv6:1; + unsigned int wan:1; + unsigned int rd:1; /* replace dscp */ + unsigned int dscp:6; + unsigned int pr:1; /* policy route */ + unsigned int prs:2; /* policy route select */ + unsigned int kv:1; + unsigned int sws:1; + unsigned int max_len:2; /* Max. length select */ + unsigned int fp:1; /* force VLAN priority */ + unsigned int pri:3; + unsigned int pd:1; + unsigned int pi:1; + unsigned int psidx:4; + unsigned int ag:2; + unsigned int unused:3; +#else + unsigned int unused:3; + unsigned int ag:2; + unsigned int psidx:4; + unsigned int pi:1; + unsigned int pd:1; + unsigned int pri:3; + unsigned int fp:1; /* force VLAN priority */ + unsigned int max_len:2; /* Max. length select */ + unsigned int sws:1; + unsigned int kv:1; + unsigned int prs:2; /* policy route select */ + unsigned int pr:1; /* policy route */ + unsigned int dscp:6; + unsigned int rd:1; /* replace dscp */ + unsigned int wan:1; + unsigned int ipv6:1; + unsigned int valid:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused_1:24; + unsigned int loc:8; +#else + unsigned int loc:8; + unsigned int unused_1:24; +#endif + + unsigned int dip[4]; + unsigned int sip[4]; + unsigned short dip_mask; + unsigned short sip_mask; + unsigned int pkt_cnt; +} SPPE_ROUTE; + +#if 0 +typedef struct _sppe_vserver { + unsigned int valid:1; + unsigned int tcp:1; + unsigned int udp:1; + unsigned int dscp_lan:6; + unsigned int dscp_wan:6; + unsigned int pri_lan:3; + unsigned int pri_wan:3; + unsigned int unused:11; + + unsigned int wanip; + unsigned int lanip; + unsigned short port_start; + unsigned short port_end; + unsigned int pkt_cnt; +} SPPE_VSERVER; +#else +typedef struct _sppe_snat { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int valid:1; + unsigned int tcp:1; + unsigned int udp:1; + unsigned int rd:1; + unsigned int dscp:6; + unsigned int fp:1; + unsigned int pri:3; + unsigned int kv:1; + unsigned int sws:1; + unsigned int max_len:2; + unsigned int pd:1; + unsigned int pi:1; + unsigned int psidx:4; + unsigned int pr:1; /* policy route */ + unsigned int prs:2; /* policy route select */ + unsigned int ag:2; + unsigned int unused:3; +#else + unsigned int unused:3; + unsigned int ag:2; + unsigned int prs:2; /* policy route select */ + unsigned int pr:1; /* policy route */ + unsigned int psidx:4; + unsigned int pi:1; + unsigned int pd:1; + unsigned int max_len:2; + unsigned int sws:1; + unsigned int kv:1; + unsigned int pri:3; + unsigned int fp:1; + unsigned int dscp:6; + unsigned int rd:1; + unsigned int udp:1; + unsigned int tcp:1; + unsigned int valid:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused_1:24; + unsigned int loc:8; +#else + unsigned int loc:8; + unsigned int unused_1:24; +#endif + + unsigned int wanip; + unsigned int lanip; + unsigned short port_start; + unsigned short port_end; + unsigned int pkt_cnt; +} SPPE_SNAT; + +typedef struct _sppe_dnat { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int valid:1; + unsigned int tcp:1; + unsigned int udp:1; + unsigned int rd:1; + unsigned int dscp:6; + unsigned int fp:1; + unsigned int pri:3; + unsigned int kv:1; + unsigned int sws:1; + unsigned int max_len:2; + unsigned int pd:1; + unsigned int pi:1; + unsigned int psidx:4; + unsigned int pr:1; /* policy route */ + unsigned int prs:2; /* policy route select */ + unsigned int ag:2; + unsigned int unused:3; +#else + unsigned int unused:3; + unsigned int ag:2; + unsigned int prs:2; /* policy route select */ + unsigned int pr:1; /* policy route */ + unsigned int psidx:4; + unsigned int pi:1; + unsigned int pd:1; + unsigned int max_len:2; + unsigned int sws:1; + unsigned int kv:1; + unsigned int pri:3; + unsigned int fp:1; + unsigned int dscp:6; + unsigned int rd:1; + unsigned int udp:1; + unsigned int tcp:1; + unsigned int valid:1; +#endif +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused_1:24; + unsigned int loc:8; +#else + unsigned int loc:8; + unsigned int unused_1:24; +#endif + + unsigned int wanip; + unsigned int lanip; + unsigned short port_start; + unsigned short port_end; + unsigned int pkt_cnt; +} SPPE_DNAT; +#endif +typedef struct _sppe_limit { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int drop_red:1; + unsigned int pass_green:1; + unsigned int force_color:1; + unsigned int color_select:2; + unsigned int time_stamp:21; + unsigned int reserved:6; +#else + unsigned int reserved:6; + unsigned int time_stamp:21; + unsigned int color_select:2; + unsigned int force_color:1; + unsigned int pass_green:1; + unsigned int drop_red:1; +#endif + unsigned short min_rate; + unsigned short max_rate; +} SPPE_LIMIT; + +typedef struct _sppe_global_rate_limit { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int valid:1; + unsigned int wan:1; + unsigned int ipv6:1; + unsigned int tcp:1; + unsigned int udp:1; + unsigned int unused:17; +#else + unsigned int unused:17; + unsigned int udp:1; + unsigned int tcp:1; + unsigned int ipv6:1; + unsigned int wan:1; + unsigned int valid:1; +#endif + + unsigned int sip[4]; + unsigned int dip[4]; + unsigned short sip_mask; + unsigned short dip_mask; + unsigned short sport_start; + unsigned short sport_end; + unsigned short dport_start; + unsigned short dport_end; + SPPE_LIMIT limit; +} SPPE_GLOBAL_RATE_LIMIT; + +/* + * SPPE_CMD_FLOW_BRIDGE_IPV4 + * type = 1 , as = 3 + */ +typedef struct _sppe_flow_bridge_ipv4 { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l4_prot:2; + unsigned int l2s:2; /* L2 select */ + unsigned int prs:2; + unsigned int kv:1; + unsigned int fp:1; + unsigned int pri:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int reserved:13; +#else + unsigned int reserved:13; + unsigned int max_len:2; /* Max. length select */ + unsigned int pri:3; + unsigned int fp:1; + unsigned int kv:1; + unsigned int prs:2; + unsigned int l2s:2; /* L2 select */ + unsigned int l4_prot:2; + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused:16; + unsigned int mac4732:16; +#else + unsigned int mac4732:16; + unsigned int unused:16; +#endif + + unsigned int mac3100; +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + unsigned int sip; + unsigned int dip; + + union { + struct { + unsigned short src; + unsigned short dst; + } port; + struct { + unsigned short call_id; + } gre; + struct { + unsigned char protocol; + } others; + } l4; + + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_BRIDGE_IPV4; + +/* + * SPPE_CMD_FLOW_BRIDGE_IPV6 + * type = 2 , as = 3 + */ +typedef struct _sppe_flow_bridge_ipv6 { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l4_prot:2; + unsigned int l2s:2; /* L2 select */ + unsigned int prs:2; + unsigned int kv:1; + unsigned int fp:1; + unsigned int pri:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int reserved:13; +#else + unsigned int reserved:13; + unsigned int max_len:2; /* Max. length select */ + unsigned int pri:3; + unsigned int fp:1; + unsigned int kv:1; + unsigned int prs:2; + unsigned int l2s:2; /* L2 select */ + unsigned int l4_prot:2; + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused:16; + unsigned int mac4732:16; +#else + unsigned int mac4732:16; + unsigned int unused:16; +#endif + + unsigned int mac3100; +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + unsigned int sip[4]; + unsigned int dip[4]; + union { + struct { + unsigned short src; + unsigned short dst; + } port; + struct { + unsigned short call_id; + } gre; + struct { + unsigned char protocol; + } others; + } l4; + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_BRIDGE_IPV6; + +/* + * SPPE_CMD_FLOW_ROUTE_IPV4 + * type = 1, as = 0 + */ +typedef struct _sppe_flow_route_ipv4 { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l4_prot:2; + unsigned int l2s:2; /* L2 select */ + unsigned int prs:2; + unsigned int kv:1; + unsigned int rd:1; + unsigned int dscp:6; + unsigned int fp:1; + unsigned int pri:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int pd:1; + unsigned int pi:1; + unsigned int psidx:4; +#else + unsigned int psidx:4; + unsigned int pi:1; + unsigned int pd:1; + unsigned int max_len:2; /* Max. length select */ + unsigned int pri:3; + unsigned int fp:1; + unsigned int dscp:6; + unsigned int rd:1; + unsigned int kv:1; + unsigned int prs:2; + unsigned int l2s:2; /* L2 select */ + unsigned int l4_prot:2; + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused:16; + unsigned int mac4732:16; +#else + unsigned int mac4732:16; + unsigned int unused:16; +#endif + unsigned int mac3100; + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + + unsigned int sip; + unsigned int dip; + union { + struct { + unsigned short src; + unsigned short dst; + } port; + struct { + unsigned short call_id; + } gre; + struct { + unsigned char protocol; + } others; + } l4; + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_ROUTE_IPV4; + +/* + * SPPE_CMD_FLOW_ROUTE_IPV6 + * type = 2, as = 0 + */ +typedef struct _sppe_flow_route_ipv6 { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l4_prot:2; + unsigned int l2s:2; /* L2 select */ + unsigned int prs:2; + unsigned int kv:1; + unsigned int rd:1; + unsigned int dscp:6; + unsigned int fp:1; + unsigned int pri:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int pd:1; + unsigned int pi:1; + unsigned int psidx:4; +#else + unsigned int psidx:4; + unsigned int pi:1; + unsigned int pd:1; + unsigned int max_len:2; /* Max. length select */ + unsigned int pri:3; + unsigned int fp:1; + unsigned int dscp:6; + unsigned int rd:1; + unsigned int kv:1; + unsigned int prs:2; + unsigned int l2s:2; /* L2 select */ + unsigned int l4_prot:2; + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused:16; + unsigned int mac4732:16; +#else + unsigned int mac4732:16; + unsigned int unused:16; +#endif + unsigned int mac3100; + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + unsigned int sip[4]; + unsigned int dip[4]; + union { + struct { + unsigned short src; + unsigned short dst; + } port; + struct { + unsigned short call_id; + } gre; + struct { + unsigned char protocol; + } others; + } l4; + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_ROUTE_IPV6; + +/* + * SPPE_CMD_FLOW_NAT_IPV4 + * type = 0, as = 1 + */ +typedef struct _sppe_flow_nat_ipv4 { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l4_prot:2; + unsigned int l2s:2; /* L2 select */ + unsigned int prs:2; + unsigned int kv:1; + unsigned int rd:1; + unsigned int dscp:6; + unsigned int fp:1; + unsigned int pri:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int pd:1; + unsigned int pi:1; + unsigned int psidx:4; +#else + unsigned int psidx:4; + unsigned int pi:1; + unsigned int pd:1; + unsigned int max_len:2; /* Max. length select */ + unsigned int pri:3; + unsigned int fp:1; + unsigned int dscp:6; + unsigned int rd:1; + unsigned int kv:1; + unsigned int prs:2; + unsigned int l2s:2; /* L2 select */ + unsigned int l4_prot:2; + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused:16; + unsigned int mac4732:16; +#else + unsigned int mac4732:16; + unsigned int unused:16; +#endif + + unsigned int mac3100; + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + + unsigned int sip; + unsigned int dip; + union { + struct { + unsigned short src; + unsigned short dst; + } port; + struct { + unsigned short call_id; + unsigned short nat_call_id; + } gre; + struct { + unsigned char protocol; + } others; + } l4; + unsigned int nat_ip; + unsigned short nat_port; + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_NAT_IPV4; + +/* + * SPPE_CMD_FLOW_NAT_IPV6 + * type = 1, as = 1 + */ +typedef struct _sppe_flow_nat_ipv6 { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l4_prot:2; + unsigned int l2s:2; /* L2 select */ + unsigned int prs:2; + unsigned int kv:1; + unsigned int rd:1; + unsigned int dscp:6; + unsigned int fp:1; + unsigned int pri:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int pd:1; + unsigned int pi:1; + unsigned int psidx:4; +#else + unsigned int psidx:4; + unsigned int pi:1; + unsigned int pd:1; + unsigned int max_len:2; /* Max. length select */ + unsigned int pri:3; + unsigned int fp:1; + unsigned int dscp:6; + unsigned int rd:1; + unsigned int kv:1; + unsigned int prs:2; + unsigned int l2s:2; /* L2 select */ + unsigned int l4_prot:2; + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused:16; + unsigned int mac4732:16; +#else + unsigned int mac4732:16; + unsigned int unused:16; +#endif + unsigned int mac3100; +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + unsigned int sip[4]; + unsigned int dip[4]; + union { + struct { + unsigned short src; + unsigned short dst; + } port; + struct { + unsigned short call_id; + unsigned short nat_call_id; + } gre; + struct { + unsigned char protocol; + } others; + } l4; + unsigned int nat_ip[4]; + unsigned short nat_port; + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_NAT_IPV6; + +/* + * SPPE_CMD_FLOW_TWICE_NAT + * type = 0, as = 2 + */ +typedef struct _sppe_flow_twice_nat { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l4_prot:2; + unsigned int l2s:2; /* L2 select */ + unsigned int prs:2; + unsigned int kv:1; + unsigned int rd:1; + unsigned int dscp:6; + unsigned int fp:1; + unsigned int pri:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int psidx:4; + unsigned int reserved:2; +#else + unsigned int reserved:2; + unsigned int psidx:4; + unsigned int max_len:2; /* Max. length select */ + unsigned int pri:3; + unsigned int fp:1; + unsigned int dscp:6; + unsigned int rd:1; + unsigned int kv:1; + unsigned int prs:2; + unsigned int l2s:2; /* L2 select */ + unsigned int l4_prot:2; + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused:16; + unsigned int mac4732:16; +#else + unsigned int mac4732:16; + unsigned int unused:16; +#endif + unsigned int mac3100; + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + unsigned int sip; + unsigned int dip; + unsigned short sport; + unsigned short dport; + unsigned int natsip; + unsigned int natdip; + unsigned short natsport; + unsigned short natdport; + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_TWICE_NAT; + +/* + * SPPE_CMD_FLOW_MULTICAST_IPV4 + * type = 0, as = 0 or 3 + */ +typedef struct _sppe_flow_multicast_ipv4 { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l2s:2; /* L2 select */ + unsigned int prs:2; + unsigned int kv:1; + unsigned int rd:1; + unsigned int dscp:6; + unsigned int fp:1; + unsigned int pri:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int bridge:1; + unsigned int reserved:7; +#else + unsigned int reserved:7; + unsigned int bridge:1; + unsigned int max_len:2; /* Max. length select */ + unsigned int pri:3; + unsigned int fp:1; + unsigned int dscp:6; + unsigned int rd:1; + unsigned int kv:1; + unsigned int prs:2; + unsigned int l2s:2; /* L2 select */ + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused:16; + unsigned int mac4732:16; +#else + unsigned int mac4732:16; + unsigned int unused:16; +#endif + unsigned int mac3100; + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + + unsigned int sip; + unsigned int dip; + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_MCAST_IPV4; + +/* + * SPPE_CMD_FLOW_MULTICAST_IPV6 + * type = 1, as = 0 or 3 + */ +typedef struct _sppe_flow_multicast_ipv6 { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l2s:2; /* L2 select */ + unsigned int prs:2; + unsigned int kv:1; + unsigned int rd:1; + unsigned int dscp:6; + unsigned int fp:1; + unsigned int pri:3; + unsigned int max_len:2; /* Max. length select */ + unsigned int bridge:1; + unsigned int reserved:7; +#else + unsigned int reserved:7; + unsigned int bridge:1; + unsigned int max_len:2; /* Max. length select */ + unsigned int pri:3; + unsigned int fp:1; + unsigned int dscp:6; + unsigned int rd:1; + unsigned int kv:1; + unsigned int prs:2; + unsigned int l2s:2; /* L2 select */ + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int unused:16; + unsigned int mac4732:16; +#else + unsigned int mac4732:16; + unsigned int unused:16; +#endif + unsigned int mac3100; + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + + unsigned int sip[4]; + unsigned int dip[4]; + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_MCAST_IPV6; + +/* + * SPPE_CMD_FLOW_LAYER_TWO + * type = 2 + */ +typedef struct _sppe_flow_bridge_l2 { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int fw:1; + unsigned int s:1; + unsigned int sws:1; + unsigned int ag:2; + unsigned int rl:1; + unsigned int l2_prot:2; + unsigned int kv:1; + unsigned int fp:1; + unsigned int pri:3; + unsigned int psidx:4; + unsigned int reserved:15; +#else + unsigned int reserved:15; + unsigned int psidx:4; + unsigned int pri:3; + unsigned int fp:1; + unsigned int kv:1; + unsigned int l2_prot:2; + unsigned int rl:1; + unsigned int ag:2; + unsigned int sws:1; + unsigned int s:1; + unsigned int fw:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int lp:1; + unsigned int fr:1; + unsigned int pm:4; + unsigned int sv:1; + unsigned int svid:12; + unsigned int cv:1; + unsigned int cvid:12; +#else + unsigned int cvid:12; + unsigned int cv:1; + unsigned int svid:12; + unsigned int sv:1; + unsigned int pm:4; + unsigned int fr:1; + unsigned int lp:1; +#endif + + unsigned short smac[3]; + unsigned short dmac[3]; + + SPPE_LIMIT limit; + unsigned int pkt_cnt; +} SPPE_FLOW_BRIDGE_L2; + +typedef struct _sppe_arl { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int vid:12; + unsigned int pmap:5; + unsigned int age:3; + unsigned int mymac:1; + unsigned int filter:1; + unsigned int reserved:10; +#else + unsigned int reserved:10; + unsigned int filter:1; + unsigned int mymac:1; + unsigned int age:3; + unsigned int pmap:5; + unsigned int vid:12; +#endif + unsigned char mac[6]; +} SPPE_ARL; + +typedef struct _sppe_init { + unsigned int flow_pre_match_paddr; + unsigned int flow_pre_match_vaddr; + unsigned int flow_body_paddr; + unsigned int flow_body_vaddr; + unsigned int flow_ext_paddr; + unsigned int flow_ext_vaddr; + unsigned int flow_size; + unsigned int arp_pre_match_paddr; + unsigned int arp_pre_match_vaddr; + unsigned int arp_body_paddr; + unsigned int arp_body_vaddr; + unsigned int arp_size; + unsigned int ipv6_napt; +} SPPE_INIT; + +typedef struct _sppe_param_t { + SPPE_CMD cmd; + SPPE_OP op; + + union { + struct { + unsigned char major; + unsigned char minor; + unsigned char very_minor; + unsigned char pre; + } sppe_version; + + SPPE_BOOL sppe_enable; + unsigned int sppe_lanip; + + struct { + unsigned int index; + unsigned int ip; + unsigned int session_id; + } sppe_wanip; + + struct { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int index:2; + unsigned int to:4; + unsigned int sv:1; + unsigned int stag_vid:12; + unsigned int cv:1; + unsigned int ctag_vid:12; +#else + unsigned int ctag_vid:12; + unsigned int cv:1; + unsigned int stag_vid:12; + unsigned int sv:1; + unsigned int to:4; + unsigned int index:2; +#endif + unsigned char mac[6]; /* MAC address */ + } sppe_prda; + + struct { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int interval:2; + unsigned int mfactor:1; + unsigned int ununsed:29; +#else + unsigned int ununsed:29; + unsigned int mfactor:1; + unsigned int interval:2; +#endif + } sppe_rlcfg; + + struct { + unsigned int index; + SPPE_PPPOE_RELAY rule; + } sppe_pppoe_relay; + + struct { + unsigned int index; + SPPE_BRIDGE rule; + } sppe_bridge; + + struct { + unsigned int index; + SPPE_ACL rule; + } sppe_acl; + + struct { + unsigned int index; + SPPE_ROUTE rule; + } sppe_route; +#if 0 + struct { + unsigned int index; + SPPE_VSERVER rule; + } sppe_vserver; +#else + struct { + unsigned int index; + SPPE_SNAT rule; + } sppe_snat; + + struct { + unsigned int index; + SPPE_DNAT rule; + } sppe_dnat; +#endif + struct { + unsigned int index; + SPPE_GLOBAL_RATE_LIMIT rule; + } sppe_grl; + + struct { + unsigned char unit; + unsigned char arp; + unsigned char bridge; + unsigned char tcp; + unsigned char udp; + unsigned char pptp; + unsigned char other; + } sppe_agingout; + + struct { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int index:2; + unsigned int reserved:20; + unsigned int max:10; +#else + unsigned int max:10; + unsigned int reserved:20; + unsigned int index:2; +#endif + } sppe_max_length; + + struct { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int v6:1; + unsigned int s:1; + unsigned int r:1; + unsigned int fr:1; + unsigned int to:4; + unsigned int unused:24; +#else + unsigned int unused:24; + unsigned int to:4; + unsigned int fr:1; + unsigned int r:1; + unsigned int s:1; + unsigned int v6:1; +#endif + +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int sv:1; + unsigned int stag_vid:12; + unsigned int cv:1; + unsigned int ctag_vid:12; + unsigned int unused_1:6; +#else + unsigned int unused_1:6; + unsigned int ctag_vid:12; + unsigned int cv:1; + unsigned int stag_vid:12; + unsigned int sv:1; +#endif + unsigned int ip[4]; + unsigned char mac[6]; + } sppe_arp; + + SPPE_ARL sppe_arl; + + struct { + unsigned int sid; + unsigned int index; + } sppe_pppoe_sid; + + SPPE_FLOW_BRIDGE_IPV4 flow_bridge_ipv4; + SPPE_FLOW_BRIDGE_IPV6 flow_bridge_ipv6; + SPPE_FLOW_ROUTE_IPV4 flow_route_ipv4; + SPPE_FLOW_ROUTE_IPV6 flow_route_ipv6; + SPPE_FLOW_NAT_IPV4 flow_nat_ipv4; + SPPE_FLOW_NAT_IPV6 flow_nat_ipv6; + SPPE_FLOW_TWICE_NAT flow_twice_nat; + SPPE_FLOW_MCAST_IPV4 flow_mcast_ipv4; + SPPE_FLOW_MCAST_IPV6 flow_mcast_ipv6; + SPPE_FLOW_BRIDGE_L2 flow_bridge_l2; + + struct { + SPPE_DUMP_TYPE type; + unsigned short key; + unsigned short way; + unsigned int raw[23]; + } sppe_dump; + + unsigned int sppe_sna_th; + + struct { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int enable:1; + unsigned int lan:6; + unsigned int wan:6; + unsigned int reserved:19; +#else + unsigned int reserved:19; + unsigned int wan:6; + unsigned int lan:6; + unsigned int enable:1; +#endif + } sppe_chgdscp; + + struct { +#ifndef CONFIG_SWITCH_BIG_ENDIAN + unsigned int enable:1; + unsigned int lan:3; + unsigned int wan:3; + unsigned int reserved:25; +#else + unsigned int reserved:25; + unsigned int wan:3; + unsigned int lan:3; + unsigned int enable:1; +#endif + } sppe_chgpri; + + struct { + int enable; + int module; + int level; + } sppe_debug; + + struct { + unsigned int offset; + unsigned int data; + } sppe_reg; + + struct { + unsigned int offset; + unsigned int data; + } sppe_sram; + + struct { + char enable; + unsigned int max; + unsigned int min; + char drop_red; + char pass_green; + } sppe_rl_flow; + + struct { + char enable; + unsigned int max; + unsigned int min; + char drop_red; + char pass_green; + } sppe_rl_rule; + + struct { + unsigned int index; + unsigned short start; + unsigned short end; + SPPE_LIMIT limit; + } sppe_bm_flow; + + struct { + unsigned int index; + unsigned int pkt_cnt; + unsigned int byte_cnt; + } sppe_accounting_group; + + struct { + unsigned int pkt_cnt; + } sppe_drop_ipcs_err; /* IP checksum error */ + + struct { + unsigned int pkt_cnt; + } sppe_drop_rate_limit; + + struct { + unsigned int pkt_cnt; + } sppe_drop_others; + + struct { + unsigned int index; + unsigned char name[16]; + struct net_device *dev; + unsigned int vid; + } sppe_pci_fp_dev; + + SPPE_INIT sppe_init; + + } data; +} SPPE_PARAM; + +extern int sppe_hook_ready; +extern int (*sppe_func_hook)(SPPE_PARAM *param); + +extern int sppe_pci_fp_ready; +extern int (*sppe_pci_fp_hook)(SPPE_PARAM *param); + +#endif /* CONFIG_CNS3XXX_SPPE */ + +#endif /* _SPPE_H_ */ --- /dev/null +++ b/include/linux/cns3xxx/switch_api.h @@ -0,0 +1,366 @@ +/******************************************************************************* + * + * Copyright (c) 2008 Cavium Networks + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Technology Support + * Star Semiconductor 4F, No.1, Chin-Shan 8th St, Hsin-Chu,300 Taiwan, R.O.C + * + ********************************************************************************/ + +#ifndef SWITCH_API_H_K +#define SWITCH_API_H_K + + +#ifndef __KERNEL__ +typedef unsigned int u32; +typedef unsigned short int u16; +typedef unsigned char u8; +typedef int s32; +#else + +#include + +#endif + + +#define CAVM_OK 0 +#define CAVM_ERR 1 +#define CAVM_NOT_FOUND 2 +#define CAVM_FOUND 3 +#define CAVM_FAIL -1 // use minus + +#define MAC_PORT0 0 +#define MAC_PORT1 1 +#define MAC_PORT2 2 +#define CPU_PORT 3 + +typedef enum +{ + + + CNS3XXX_ARL_TABLE_LOOKUP, + CNS3XXX_ARL_TABLE_ADD, + CNS3XXX_ARL_TABLE_DEL, + CNS3XXX_ARL_TABLE_SEARCH, + CNS3XXX_ARL_TABLE_SEARCH_AGAIN, + CNS3XXX_ARL_IS_TABLE_END, + CNS3XXX_ARL_TABLE_FLUSH, + + CNS3XXX_VLAN_TABLE_LOOKUP, + CNS3XXX_VLAN_TABLE_ADD, + CNS3XXX_VLAN_TABLE_DEL, + CNS3XXX_VLAN_TABLE_READ, + + CNS3XXX_SKEW_SET, + CNS3XXX_SKEW_GET, + + CNS3XXX_BRIDGE_SET, + CNS3XXX_BRIDGE_GET, + + CNS3XXX_PORT_NEIGHBOR_SET, + CNS3XXX_PORT_NEIGHBOR_GET, + + CNS3XXX_HOL_PREVENT_SET, + CNS3XXX_HOL_PREVENT_GET, + + CNS3XXX_TC_SET, // traffic class, for 1, 2, 4, traffic class + CNS3XXX_TC_GET, + + CNS3XXX_PRI_CTRL_SET, + CNS3XXX_PRI_CTRL_GET, + + CNS3XXX_DMA_RING_CTRL_SET, + CNS3XXX_DMA_RING_CTRL_GET, + + CNS3XXX_PRI_IP_DSCP_SET, + CNS3XXX_PRI_IP_DSCP_GET, + + CNS3XXX_ETYPE_SET, + CNS3XXX_ETYPE_GET, + + CNS3XXX_UDP_RANGE_SET, + CNS3XXX_UDP_RANGE_GET, + + CNS3XXX_ARP_REQUEST_SET, + CNS3XXX_ARP_REQUEST_GET, + + CNS3XXX_RATE_LIMIT_SET, + CNS3XXX_RATE_LIMIT_GET, + + CNS3XXX_QUEUE_WEIGHT_SET, + CNS3XXX_QUEUE_WEIGHT_GET, + + CNS3XXX_FC_RLS_SET, + CNS3XXX_FC_RLS_GET, + + CNS3XXX_FC_SET_SET, + CNS3XXX_FC_SET_GET, + + CNS3XXX_SARL_RLS_SET, + CNS3XXX_SARL_RLS_GET, + + CNS3XXX_SARL_SET_SET, + CNS3XXX_SARL_SET_GET, + + CNS3XXX_SARL_OQ_SET, + CNS3XXX_SARL_OQ_GET, + + CNS3XXX_SARL_ENABLE_SET, + CNS3XXX_SARL_ENABLE_GET, + + CNS3XXX_FC_SET, + CNS3XXX_FC_GET, + + CNS3XXX_IVL_SET, + CNS3XXX_IVL_GET, + + CNS3XXX_WAN_PORT_SET, + CNS3XXX_WAN_PORT_GET, + + CNS3XXX_PVID_GET, + CNS3XXX_PVID_SET, + + CNS3XXX_QA_GET, // queue allocate + CNS3XXX_QA_SET, + + CNS3XXX_PACKET_MAX_LEN_GET, // set maximun frame length. + CNS3XXX_PACKET_MAX_LEN_SET, + + CNS3XXX_BCM53115M_REG_READ, + CNS3XXX_BCM53115M_REG_WRITE, + + CNS3XXX_RXRING_STATUS, + CNS3XXX_TXRING_STATUS, + + CNS3XXX_DUMP_MIB_COUNTER, + + CNS3XXX_REG_READ, + CNS3XXX_REG_WRITE, + +}CNS3XXXIoctlCmd; + +typedef struct +{ + u8 vlan_index; + u8 valid; + u16 vid; + u8 wan_side; + u8 etag_pmap; + u8 mb_pmap; + //u8 my_mac[6]; + u8 *my_mac; +}VLANTableEntry; // for vlan table function + +typedef struct +{ + u16 vid; + u8 pmap; + //u8 mac[6]; + u8 *mac; + u8 age_field; + u8 vlan_mac; + u8 filter; +}ARLTableEntry; // for arl table function + + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + ARLTableEntry entry; +}CNS3XXXARLTableEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + VLANTableEntry entry; +}CNS3XXXVLANTableEntry; // for ioctl VLAN table ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + u8 enable; +}CNS3XXXHOLPreventControl; + + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char which_port; // 0, 1, 2, 3 (cpu port) + unsigned char type; // 0: C-Neighbor, 1: S-Neighbor +}CNS3XXXPortNeighborControl; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char type; // 0: C-Component, 1: S-Component +}CNS3XXXBridgeControl; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char tc; // traffic class, for 1, 2, 4, traffic class +}CNS3XXXTrafficClassControl; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char which_port; // 0, 1, 2, 3 (cpu port) + unsigned int val; + unsigned char port_pri; + unsigned char udp_pri_en; + unsigned char dscp_pri_en; + unsigned char vlan_pri_en; + unsigned char ether_pri_en; +}CNS3XXXPriCtrlControl; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char ts_double_ring_en; + unsigned char fs_double_ring_en; + unsigned char fs_pkt_allocate; +}CNS3XXXDmaRingCtrlControl; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned int ip_dscp_num; // 0 ~ 63 + unsigned char pri; // 3 bits +}CNS3XXXPriIpDscpControl; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned int etype_num; + unsigned int val; + unsigned int pri; +}CNS3XXXEtypeControl; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned int udp_range_num; + unsigned short int port_start; + unsigned short int port_end; +}CNS3XXXUdpRangeEtypeControl; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char val; // 0: boradcast forward, 1: redirect to the CPU +}CNS3XXXArpRequestControl; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char which_port; // 0, 1, 2, 3 (port 0 extra dma) + unsigned char band_width; + unsigned char base_rate; + +}CNS3XXXRateLimitEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char which_port; // 0, 1, 2, 3 (port 0 extra dma) + unsigned char sch_mode; + unsigned char q0_w; + unsigned char q1_w; + unsigned char q2_w; + unsigned char q3_w; +}CNS3XXXQueueWeightEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned int val; + unsigned char tc; // 0-3 + unsigned char gyr; // 0 (green), 1(yellow), 2(red) +}CNS3XXXSARLEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char port; // 0, 1, 2, 3 (cpu port) + unsigned char fc_en; // 0(rx/tx disable), 1(rx enable), 2(tx enable), 3(rx/tx enable) +}CNS3XXXFCEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char enable; // enable: 1 -> IVL, enable: 0 -> SVL +}CNS3XXXIVLEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char wan_port; +}CNS3XXXWANPortEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char which_port; + unsigned int pvid; +}CNS3XXXPVIDEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char qa; // queue allocate +}CNS3XXXQAEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + unsigned char max_len; // maximum frame length +}CNS3XXXMaxLenEntry; // for ioctl arl ... + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + u8 page; + u8 offset; + u32 u32_val; + u16 u16_val; + u8 u8_val; + u8 data_len; + +}CNS3XXXBCM53115M; + +typedef struct +{ + CNS3XXXIoctlCmd cmd; + u32 mib[52]; + u16 mib_len; +}CNS3XXXMIBCounter; + +#if 0 +typedef struct +{ + CNS3XXXIoctlCmd cmd; + TXRing *tx_ring; + RXRing *rx_ring; +}CNS3XXXRingStatus; +#endif + + +#endif --- a/net/core/dev.c +++ b/net/core/dev.c @@ -133,6 +133,10 @@ #include "net-sysfs.h" +#if defined (CONFIG_CNS3XXX_SPPE) +#include +#endif + /* Instead of increasing this, you should create a hash table. */ #define MAX_GRO_SKBS 8 @@ -1944,6 +1948,197 @@ int weight_p __read_mostly = 64; DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; +#if defined (CONFIG_CNS3XXX_SPPE) +static struct net_device *tun_netdev = NULL; + +int sppe_pci_fp(struct sk_buff *skb) +{ + SPPE_PARAM param; + struct iphdr *iph; +#if defined (CONFIG_IPV6) + struct ipv6hdr *ipv6h; +#endif + struct tcphdr *th; + struct udphdr *uh; + int pci_dev_index; + + if (!sppe_hook_ready) { + goto NOT_IN_FP; + } + + if (!sppe_pci_fp_ready) { + goto NOT_IN_FP; + } + + /* check device packet comes from, is a registed device? */ + memset(¶m, 0, sizeof(SPPE_PARAM)); + param.cmd = SPPE_CMD_PCI_FP_DEV; + param.op = SPPE_OP_GET; + param.data.sppe_pci_fp_dev.dev = skb->dev; + sppe_pci_fp_hook(¶m); + + pci_dev_index = param.data.sppe_pci_fp_dev.index; + + if ((-1) == pci_dev_index) { + goto NOT_IN_FP; + } + + if (!tun_netdev) { + tun_netdev = dev_get_by_name(&init_net, "fp"); + + if (!tun_netdev) { + goto NOT_IN_FP; + } + } + + /* check PPE status */ + memset(¶m, 0, sizeof(SPPE_PARAM)); + param.cmd = SPPE_CMD_ENABLE; + param.op = SPPE_OP_GET; + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to get PPE status!!\n", __FUNCTION__); + goto NOT_IN_FP; + } + + if (!param.data.sppe_enable) { + goto NOT_IN_FP; + } + + memset(¶m, 0, sizeof(SPPE_PARAM)); + + switch (htons(skb->protocol)) { + case ETH_P_IP: + iph = (struct iphdr *)skb->data; + + if (5 != iph->ihl) { goto NOT_IN_FP; } + + if (iph->frag_off & 0x20) { goto NOT_IN_FP; } + + param.cmd = SPPE_CMD_FLOW_NAT_IPV4; + param.op = SPPE_OP_GET; + + param.data.flow_nat_ipv4.sip = ntohl(iph->saddr); + param.data.flow_nat_ipv4.dip = ntohl(iph->daddr); + + switch (iph->protocol) { + case IPPROTO_TCP: + th = (struct tcphdr *) ((int *)iph + 5); /* IP header length is 20 */ + + if ((th->syn) || (th->fin) || (th->rst)) { goto NOT_IN_FP; } + + param.data.flow_nat_ipv4.l4_prot = SPPE_PROT_TCP; + param.data.flow_nat_ipv4.l4.port.src = ntohs(th->source); + param.data.flow_nat_ipv4.l4.port.dst = ntohs(th->dest); + break; + case IPPROTO_UDP: + uh = (struct udphdr *) ((int *)iph + 5); /* IP header length is 20 */ + param.data.flow_nat_ipv4.l4_prot = SPPE_PROT_UDP; + param.data.flow_nat_ipv4.l4.port.src = ntohs(uh->source); + param.data.flow_nat_ipv4.l4.port.dst = ntohs(uh->dest); + break; + default: + goto NOT_IN_FP; + } + + if (SPPE_RESULT_SUCCESS != sppe_func_hook(¶m)) { + goto NOT_IN_FP; + } else { + struct ethhdr *eth; + + eth = (struct ethhdr *)skb->mac_header; + + memset(¶m, 0, sizeof(SPPE_PARAM)); + param.cmd = SPPE_CMD_ARP; + param.op = SPPE_OP_SET; + param.data.sppe_arp.s = 1; + param.data.sppe_arp.ip[0] = iph->saddr; + param.data.sppe_arp.mac[0] = eth->h_source[0]; + param.data.sppe_arp.mac[1] = eth->h_source[1]; + param.data.sppe_arp.mac[2] = eth->h_source[2]; + param.data.sppe_arp.mac[3] = eth->h_source[3]; + param.data.sppe_arp.mac[4] = eth->h_source[4]; + param.data.sppe_arp.mac[5] = eth->h_source[5]; + param.data.sppe_arp.unused_1 = pci_dev_index; + + if (SPPE_RESULT_SUCCESS != sppe_func_hook(¶m)) { + printk("add ARP fail\n"); + #if 0 + } else { + param.data.sppe_arp.unused_1 = 0xf; + param.op = SPPE_OP_GET; + if (SPPE_RESULT_SUCCESS != sppe_func_hook(¶m)) { + printk("read ARP fail\n"); + } else { + printk("param.data.sppe_arp.unused_1 %d\n", param.data.sppe_arp.unused_1); + } + #endif + } + } + break; /* case ETH_P_IP: */ +#if defined (CONFIG_IPV6) + case ETH_P_IPV6: + ipv6h = (struct ipv6hdr *)skb->data; + switch (ipv6h->nexthdr) { + case IPPROTO_TCP: + th = (struct tcphdr *) ((int *)ipv6h + 10); /* IPv6 header length is 40 bytes */ + + if ((th->syn) || (th->fin) || (th->rst)) { goto NOT_IN_FP; } + + param.data.flow_route_ipv6.l4_prot = SPPE_PROT_TCP; + param.data.flow_route_ipv6.l4.port.src = ntohs(th->source); + param.data.flow_route_ipv6.l4.port.dst = ntohs(th->dest); + param.data.flow_route_ipv6.l4_prot = SPPE_PROT_TCP; + break; + case IPPROTO_UDP: + uh = (struct udphdr *) ((int *)ipv6h + 10); /* IPv6 header length is 40 byte */ + param.data.flow_route_ipv6.l4_prot = SPPE_PROT_UDP; + param.data.flow_route_ipv6.l4.port.src = ntohs(uh->source); + param.data.flow_route_ipv6.l4.port.dst = ntohs(uh->dest); + break; + default: + goto NOT_IN_FP; + } + + param.data.flow_route_ipv6.sip[0] = ntohl(ipv6h->saddr.s6_addr32[0]); + param.data.flow_route_ipv6.sip[1] = ntohl(ipv6h->saddr.s6_addr32[1]); + param.data.flow_route_ipv6.sip[2] = ntohl(ipv6h->saddr.s6_addr32[2]); + param.data.flow_route_ipv6.sip[3] = ntohl(ipv6h->saddr.s6_addr32[3]); + param.data.flow_route_ipv6.dip[0] = ntohl(ipv6h->daddr.s6_addr32[0]); + param.data.flow_route_ipv6.dip[1] = ntohl(ipv6h->daddr.s6_addr32[1]); + param.data.flow_route_ipv6.dip[2] = ntohl(ipv6h->daddr.s6_addr32[2]); + param.data.flow_route_ipv6.dip[3] = ntohl(ipv6h->daddr.s6_addr32[3]); + + param.cmd = SPPE_CMD_FLOW_ROUTE_IPV6; + param.op = SPPE_OP_GET; + + if (SPPE_RESULT_SUCCESS != sppe_func_hook(¶m)) { + goto NOT_IN_FP; + } + + break; /* case ETH_P_IPV6: */ +#endif + case ETH_P_PPP_SES: + break; + default: /* unsupport protocol */ + goto NOT_IN_FP; + } + /* Update counter */ + skb->dev = tun_netdev; + skb->ip_summed = CHECKSUM_NONE; + skb_push(skb, ETH_HLEN); + + dev_queue_xmit(skb); + +return 0; + +NOT_IN_FP: + return (-1); +} +#endif + + + /** * netif_rx - post buffer to the network code @@ -1965,6 +2160,12 @@ int netif_rx(struct sk_buff *skb) struct softnet_data *queue; unsigned long flags; +#if defined (CONFIG_CNS3XXX_SPPE) + if (0 == sppe_pci_fp(skb)) { + return NET_RX_SUCCESS; + } +#endif + /* if netpoll wants it, pretend we never saw it */ if (netpoll_rx(skb)) return NET_RX_DROP; @@ -2259,6 +2460,12 @@ int netif_receive_skb(struct sk_buff *sk if (!skb->tstamp.tv64) net_timestamp(skb); +#if defined (CONFIG_CNS3XXX_SPPE) + if (0 == sppe_pci_fp(skb)) { + return NET_RX_SUCCESS; + } +#endif + if (skb->vlan_tci && vlan_hwaccel_do_receive(skb)) return NET_RX_SUCCESS; --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -42,6 +42,9 @@ #include #include #include +#if defined (CONFIG_CNS3XXX_SPPE) +#include +#endif #define NF_CONNTRACK_VERSION "0.5.0" @@ -275,6 +278,92 @@ void nf_ct_insert_dying_list(struct nf_c } EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list); +#if defined (CONFIG_CNS3XXX_SPPE) +static int sppe_flow_del(struct nf_conn *ct) +{ + if (sppe_hook_ready) { + SPPE_PARAM param; + + struct nf_conntrack_tuple *orig, *reply; + + orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + reply = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; + + if (PF_INET == orig->src.l3num) { + param.cmd = SPPE_CMD_FLOW_NAT_IPV4; + } else if (PF_INET6 == orig->src.l3num) { + param.cmd = SPPE_CMD_FLOW_ROUTE_IPV6; + } else { + goto SPPE_FLOW_DEL_FINI; + } + + if (IPPROTO_TCP == orig->dst.protonum) { + param.data.flow_nat_ipv4.l4_prot = SPPE_PROT_TCP; + } else if (IPPROTO_UDP == orig->dst.protonum) { + param.data.flow_nat_ipv4.l4_prot = SPPE_PROT_UDP; + } else if (IPPROTO_GRE == orig->dst.protonum) { + param.data.flow_nat_ipv4.l4_prot = SPPE_PROT_PPTP_GRE; + } else { + goto SPPE_FLOW_DEL_FINI; + } + + param.op = SPPE_OP_DELETE_OUTDATED; + + param.data.flow_nat_ipv4.fw = 0; + if (SPPE_CMD_FLOW_ROUTE_IPV6 == param.cmd) { + param.data.flow_route_ipv6.sip[0] = htonl(orig->src.u3.ip6[0]); + param.data.flow_route_ipv6.sip[1] = htonl(orig->src.u3.ip6[1]); + param.data.flow_route_ipv6.sip[2] = htonl(orig->src.u3.ip6[2]); + param.data.flow_route_ipv6.sip[3] = htonl(orig->src.u3.ip6[3]); + param.data.flow_route_ipv6.dip[0] = htonl(orig->dst.u3.ip6[0]); + param.data.flow_route_ipv6.dip[1] = htonl(orig->dst.u3.ip6[1]); + param.data.flow_route_ipv6.dip[2] = htonl(orig->dst.u3.ip6[2]); + param.data.flow_route_ipv6.dip[3] = htonl(orig->dst.u3.ip6[3]); + param.data.flow_route_ipv6.l4.port.src = htons(orig->src.u.tcp.port); + param.data.flow_route_ipv6.l4.port.dst = htons(orig->dst.u.tcp.port); + } else { + param.data.flow_nat_ipv4.sip = htonl(orig->src.u3.ip); + param.data.flow_nat_ipv4.dip = htonl(orig->dst.u3.ip); + param.data.flow_nat_ipv4.l4.port.src = htons(orig->src.u.tcp.port); + param.data.flow_nat_ipv4.l4.port.dst = htons(orig->dst.u.tcp.port); + } + + if (SPPE_RESULT_FAIL == sppe_func_hook(¶m)) { + return (-1); + } + + param.data.flow_nat_ipv4.fw = 1; + + if (SPPE_CMD_FLOW_ROUTE_IPV6 == param.cmd) { + param.data.flow_route_ipv6.sip[0] = htonl(reply->src.u3.ip6[0]); + param.data.flow_route_ipv6.sip[1] = htonl(reply->src.u3.ip6[1]); + param.data.flow_route_ipv6.sip[2] = htonl(reply->src.u3.ip6[2]); + param.data.flow_route_ipv6.sip[3] = htonl(reply->src.u3.ip6[3]); + param.data.flow_route_ipv6.dip[0] = htonl(reply->dst.u3.ip6[0]); + param.data.flow_route_ipv6.dip[1] = htonl(reply->dst.u3.ip6[1]); + param.data.flow_route_ipv6.dip[2] = htonl(reply->dst.u3.ip6[2]); + param.data.flow_route_ipv6.dip[3] = htonl(reply->dst.u3.ip6[3]); + + param.data.flow_route_ipv6.l4.port.src = htons(reply->src.u.tcp.port); + param.data.flow_route_ipv6.l4.port.dst = htons(reply->dst.u.tcp.port); + } else { + param.data.flow_nat_ipv4.sip = htonl(reply->src.u3.ip); + param.data.flow_nat_ipv4.dip = htonl(reply->dst.u3.ip); + param.data.flow_nat_ipv4.l4.port.src = htons(reply->src.u.tcp.port); + param.data.flow_nat_ipv4.l4.port.dst = htons(reply->dst.u.tcp.port); + } + + if (SPPE_RESULT_FAIL == sppe_func_hook(¶m)) { + return (-1); + } + } + +SPPE_FLOW_DEL_FINI: + return 0; +} +#endif + + static void death_by_timeout(unsigned long ul_conntrack) { struct nf_conn *ct = (void *)ul_conntrack; @@ -289,6 +378,16 @@ static void death_by_timeout(unsigned lo set_bit(IPS_DYING_BIT, &ct->status); nf_ct_delete_from_lists(ct); nf_ct_put(ct); + +#if defined (CONFIG_CNS3XXX_SPPE) + if (sppe_flow_del(ct)) { + #if 0 + ct->timeout.expires = jiffies + (120*HZ); + add_timer(&ct->timeout); + #endif + } +#endif + } /* --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -40,6 +40,10 @@ #include #include +#if defined (CONFIG_CNS3XXX_SPPE) +#include +#endif + #define GRE_TIMEOUT (30 * HZ) #define GRE_STREAM_TIMEOUT (180 * HZ) @@ -226,6 +230,57 @@ static int gre_print_conntrack(struct se (ct->proto.gre.stream_timeout / HZ)); } +#if defined (CONFIG_CNS3XXX_SPPE) +static int sppe_gre_flow_add(struct nf_conn *ct) +{ + SPPE_PARAM param; + struct nf_conntrack_tuple *orig, *reply; + + if (0 == sppe_hook_ready) { + return 0; + } + + memset(¶m, 0, sizeof(SPPE_PARAM)); + + orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + reply = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; + + param.cmd = SPPE_CMD_FLOW_NAT_IPV4; + param.op = SPPE_OP_SET; + + param.data.flow_nat_ipv4.fw = 0; + param.data.flow_nat_ipv4.sip = htonl(orig->src.u3.ip); + param.data.flow_nat_ipv4.dip = htonl(orig->dst.u3.ip); + + param.data.flow_nat_ipv4.l4_prot = SPPE_PROT_PPTP_GRE; + param.data.flow_nat_ipv4.l4.gre.call_id = htons(orig->dst.u.gre.key); + + param.data.flow_nat_ipv4.nat_ip = htonl(reply->dst.u3.ip); + param.data.flow_nat_ipv4.l4.gre.nat_call_id = htons(reply->src.u.gre.key); + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv4 from-LAN flow!!\n", __FUNCTION__); + } + + param.data.flow_nat_ipv4.fw = 1; + + param.data.flow_nat_ipv4.sip = htonl(reply->src.u3.ip); + param.data.flow_nat_ipv4.dip = htonl(reply->dst.u3.ip); + param.data.flow_nat_ipv4.l4.gre.call_id = htons(reply->dst.u.gre.key); + + param.data.flow_nat_ipv4.nat_ip = htonl(orig->src.u3.ip); + param.data.flow_nat_ipv4.l4.gre.nat_call_id = htons(orig->src.u.gre.key); + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv4 from-WAN flow!!\n", __FUNCTION__); + } + + return 0; +} +#endif + + + /* Returns verdict for packet, and may modify conntrack */ static int gre_packet(struct nf_conn *ct, const struct sk_buff *skb, @@ -242,6 +297,10 @@ static int gre_packet(struct nf_conn *ct /* Also, more likely to be important, and not a probe. */ set_bit(IPS_ASSURED_BIT, &ct->status); nf_conntrack_event_cache(IPCT_STATUS, ct); +#if defined (CONFIG_CNS3XXX_SPPE) + sppe_gre_flow_add(ct); +#endif + } else nf_ct_refresh_acct(ct, ctinfo, skb, ct->proto.gre.timeout); --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -29,6 +29,10 @@ #include #include +#if defined (CONFIG_CNS3XXX_SPPE) +#include +#endif + /* "Be conservative in what you do, be liberal in what you accept from others." If it's non-zero, we mark only out of window RST segments as INVALID. */ @@ -814,6 +818,141 @@ static int tcp_error(struct net *net, return NF_ACCEPT; } +#if defined (CONFIG_CNS3XXX_SPPE) +static int sppe_tcp_flow_add_ipv4(struct nf_conn *ct) +{ + SPPE_PARAM param; + struct nf_conntrack_tuple *orig, *reply; + + orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + reply = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; + +#if defined (CONFIG_NF_CONNTRACK_PPTP) + if (1723 == htons(orig->dst.u.tcp.port)) { + /* PPTP Control Protocol, PPTP GRE tunneling need this kind of packet */ + return 0; + } +#endif +#if defined (CONFIG_NF_CONNTRACK_FTP) + if (21 == htons(orig->dst.u.tcp.port)) { + /* PPTP Control Protocol, PPTP GRE tunneling need this kind of packet */ + return 0; + } +#endif + + memset(¶m, 0, sizeof(SPPE_PARAM)); + + param.cmd = SPPE_CMD_FLOW_NAT_IPV4; + param.op = SPPE_OP_SET; + + param.data.flow_nat_ipv4.fw = 0; + param.data.flow_nat_ipv4.sip = htonl(orig->src.u3.ip); + param.data.flow_nat_ipv4.dip = htonl(orig->dst.u3.ip); + + param.data.flow_nat_ipv4.l4_prot = SPPE_PROT_TCP; + param.data.flow_nat_ipv4.l4.port.src = htons(orig->src.u.tcp.port); + param.data.flow_nat_ipv4.l4.port.dst = htons(orig->dst.u.tcp.port); + + param.data.flow_nat_ipv4.nat_ip = htonl(reply->dst.u3.ip); + param.data.flow_nat_ipv4.nat_port = htons(reply->dst.u.tcp.port); + param.data.flow_nat_ipv4.max_len = 0x3; + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv4 from-LAN flow!!\n", __FUNCTION__); + } + + param.data.flow_nat_ipv4.fw = 1; + param.data.flow_nat_ipv4.sip = htonl(reply->src.u3.ip); + param.data.flow_nat_ipv4.dip = htonl(reply->dst.u3.ip); + param.data.flow_nat_ipv4.l4.port.src = htons(reply->src.u.tcp.port); + param.data.flow_nat_ipv4.l4.port.dst = htons(reply->dst.u.tcp.port); + + param.data.flow_nat_ipv4.nat_ip = htonl(orig->src.u3.ip); + param.data.flow_nat_ipv4.nat_port = htons(orig->src.u.tcp.port); + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv4 from-WAN flow!!\n", __FUNCTION__); + } + + return 0; +} + +static int sppe_tcp_flow_add_ipv6(struct nf_conn *ct) +{ + SPPE_PARAM param; + struct nf_conntrack_tuple *orig, *reply; + + orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + reply = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; + + if (1723 == htons(orig->dst.u.tcp.port)) { + /* PPTP Control Protocol, PPTP GRE tunneling need this kind of packet */ + return 0; + } + + memset(¶m, 0, sizeof(SPPE_PARAM)); + + param.cmd = SPPE_CMD_FLOW_ROUTE_IPV6; + param.op = SPPE_OP_SET; + + /* from-LAN flow */ + param.data.flow_route_ipv6.fw = 0; + param.data.flow_route_ipv6.sip[0] = htonl(orig->src.u3.ip6[0]); + param.data.flow_route_ipv6.sip[1] = htonl(orig->src.u3.ip6[1]); + param.data.flow_route_ipv6.sip[2] = htonl(orig->src.u3.ip6[2]); + param.data.flow_route_ipv6.sip[3] = htonl(orig->src.u3.ip6[3]); + param.data.flow_route_ipv6.dip[0] = htonl(orig->dst.u3.ip6[0]); + param.data.flow_route_ipv6.dip[1] = htonl(orig->dst.u3.ip6[1]); + param.data.flow_route_ipv6.dip[2] = htonl(orig->dst.u3.ip6[2]); + param.data.flow_route_ipv6.dip[3] = htonl(orig->dst.u3.ip6[3]); + param.data.flow_route_ipv6.l4_prot = SPPE_PROT_TCP; + param.data.flow_route_ipv6.l4.port.src = htons(orig->src.u.tcp.port); + param.data.flow_route_ipv6.l4.port.dst = htons(orig->dst.u.tcp.port); + param.data.flow_route_ipv6.max_len = 0x3; + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv6 from-LAN flow!!\n", __FUNCTION__); + } + + /* from-WAN flow */ + param.data.flow_route_ipv6.fw = 1; + param.data.flow_route_ipv6.sip[0] = htonl(reply->src.u3.ip6[0]); + param.data.flow_route_ipv6.sip[1] = htonl(reply->src.u3.ip6[1]); + param.data.flow_route_ipv6.sip[2] = htonl(reply->src.u3.ip6[2]); + param.data.flow_route_ipv6.sip[3] = htonl(reply->src.u3.ip6[3]); + param.data.flow_route_ipv6.dip[0] = htonl(reply->dst.u3.ip6[0]); + param.data.flow_route_ipv6.dip[1] = htonl(reply->dst.u3.ip6[1]); + param.data.flow_route_ipv6.dip[2] = htonl(reply->dst.u3.ip6[2]); + param.data.flow_route_ipv6.dip[3] = htonl(reply->dst.u3.ip6[3]); + param.data.flow_route_ipv6.l4.port.src = htons(reply->src.u.tcp.port); + param.data.flow_route_ipv6.l4.port.dst = htons(reply->dst.u.tcp.port); + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv6 from-LAN flow!!\n", __FUNCTION__); + } + + return 0; +} + +static int sppe_tcp_flow_add(struct nf_conn *ct) +{ + if (0 == sppe_hook_ready) { + return 0; + } + + if (AF_INET == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num) { + sppe_tcp_flow_add_ipv4(ct); + return 0; + } else if (AF_INET6 == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num) { + sppe_tcp_flow_add_ipv6(ct); + return 0; + } + + /* return fail */ + return (-1); +} +#endif + /* Returns verdict for packet, or -1 for invalid. */ static int tcp_packet(struct nf_conn *ct, const struct sk_buff *skb, @@ -961,11 +1100,18 @@ static int tcp_packet(struct nf_conn *ct break; } +#if defined (CONFIG_CNS3XXX_SPPE) + if(!(th->rst == 1 || th->fin == 1)) { +#endif if (!tcp_in_window(ct, &ct->proto.tcp, dir, index, skb, dataoff, th, pf)) { spin_unlock_bh(&ct->lock); return -NF_ACCEPT; } +#if defined (CONFIG_CNS3XXX_SPPE) + } +#endif + in_window: /* From now on we have got in-window packets */ ct->proto.tcp.last_index = index; @@ -1015,6 +1161,10 @@ static int tcp_packet(struct nf_conn *ct connection. */ set_bit(IPS_ASSURED_BIT, &ct->status); nf_conntrack_event_cache(IPCT_STATUS, ct); +#if defined (CONFIG_CNS3XXX_SPPE) + /* Add SPPE hardware flow */ + sppe_tcp_flow_add(ct); +#endif } nf_ct_refresh_acct(ct, ctinfo, skb, timeout); --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -24,6 +24,9 @@ #include #include #include +#if defined (CONFIG_CNS3XXX_SPPE) +#include +#endif static unsigned int nf_ct_udp_timeout __read_mostly = 30*HZ; static unsigned int nf_ct_udp_timeout_stream __read_mostly = 180*HZ; @@ -63,6 +66,122 @@ static int udp_print_tuple(struct seq_fi ntohs(tuple->dst.u.udp.port)); } +#if defined (CONFIG_CNS3XXX_SPPE) +static int sppe_udp_flow_add_ipv4(struct nf_conn *ct) +{ + SPPE_PARAM param; + struct nf_conntrack_tuple *orig, *reply; + + orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + reply = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; + + memset(¶m, 0, sizeof(SPPE_PARAM)); + + param.cmd = SPPE_CMD_FLOW_NAT_IPV4; + param.op = SPPE_OP_SET; + + param.data.flow_nat_ipv4.fw = 0; + param.data.flow_nat_ipv4.sip = htonl(orig->src.u3.ip); + param.data.flow_nat_ipv4.dip = htonl(orig->dst.u3.ip); + param.data.flow_nat_ipv4.l4_prot = SPPE_PROT_UDP; + + param.data.flow_nat_ipv4.l4.port.src = htons(orig->src.u.tcp.port); + param.data.flow_nat_ipv4.l4.port.dst = htons(orig->dst.u.tcp.port); + + param.data.flow_nat_ipv4.nat_ip = htonl(reply->dst.u3.ip); + param.data.flow_nat_ipv4.nat_port = htons(reply->dst.u.tcp.port); + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv4 UDP from-LAN flow!!\n", __FUNCTION__); + } + param.data.flow_nat_ipv4.fw = 1; + param.data.flow_nat_ipv4.sip = htonl(reply->src.u3.ip); + param.data.flow_nat_ipv4.dip = htonl(reply->dst.u3.ip); + + param.data.flow_nat_ipv4.l4.port.src = htons(reply->src.u.tcp.port); + param.data.flow_nat_ipv4.l4.port.dst = htons(reply->dst.u.tcp.port); + + param.data.flow_nat_ipv4.nat_ip = htonl(orig->src.u3.ip); + param.data.flow_nat_ipv4.nat_port = htons(orig->src.u.tcp.port); + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv4 from-WAN flow!!\n", __FUNCTION__); + } + + return 0; +} + +static int sppe_udp_flow_add_ipv6(struct nf_conn *ct) +{ + SPPE_PARAM param; + struct nf_conntrack_tuple *orig, *reply; + + orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + reply = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; + + memset(¶m, 0, sizeof(SPPE_PARAM)); + + param.cmd = SPPE_CMD_FLOW_ROUTE_IPV6; + param.op = SPPE_OP_SET; + + /* from-LAN flow */ + param.data.flow_route_ipv6.fw = 0; + param.data.flow_route_ipv6.sip[0] = htonl(orig->src.u3.ip6[0]); + param.data.flow_route_ipv6.sip[1] = htonl(orig->src.u3.ip6[1]); + param.data.flow_route_ipv6.sip[2] = htonl(orig->src.u3.ip6[2]); + param.data.flow_route_ipv6.sip[3] = htonl(orig->src.u3.ip6[3]); + param.data.flow_route_ipv6.dip[0] = htonl(orig->dst.u3.ip6[0]); + param.data.flow_route_ipv6.dip[1] = htonl(orig->dst.u3.ip6[1]); + param.data.flow_route_ipv6.dip[2] = htonl(orig->dst.u3.ip6[2]); + param.data.flow_route_ipv6.dip[3] = htonl(orig->dst.u3.ip6[3]); + param.data.flow_route_ipv6.l4_prot = SPPE_PROT_UDP; + param.data.flow_route_ipv6.l4.port.src = htons(orig->src.u.udp.port); + param.data.flow_route_ipv6.l4.port.dst = htons(orig->dst.u.udp.port); + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv6 from-LAN flow!!\n", __FUNCTION__); + } + + /* from-WAN flow */ + param.data.flow_route_ipv6.fw = 1; + param.data.flow_route_ipv6.sip[0] = htonl(reply->src.u3.ip6[0]); + param.data.flow_route_ipv6.sip[1] = htonl(reply->src.u3.ip6[1]); + param.data.flow_route_ipv6.sip[2] = htonl(reply->src.u3.ip6[2]); + param.data.flow_route_ipv6.sip[3] = htonl(reply->src.u3.ip6[3]); + param.data.flow_route_ipv6.dip[0] = htonl(reply->dst.u3.ip6[0]); + param.data.flow_route_ipv6.dip[1] = htonl(reply->dst.u3.ip6[1]); + param.data.flow_route_ipv6.dip[2] = htonl(reply->dst.u3.ip6[2]); + param.data.flow_route_ipv6.dip[3] = htonl(reply->dst.u3.ip6[3]); + param.data.flow_route_ipv6.l4.port.src = htons(reply->src.u.udp.port); + param.data.flow_route_ipv6.l4.port.dst = htons(reply->dst.u.udp.port); + + if (sppe_func_hook(¶m)) { + printk("<0><%s> fail to add IPv6 from-LAN flow!!\n", __FUNCTION__); + } + + return 0; +} + +static int sppe_udp_flow_add(struct nf_conn *ct) +{ + if (0 == sppe_hook_ready) { + return 0; + } + + if (AF_INET == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num) { + sppe_udp_flow_add_ipv4(ct); + return 0; + } else if (AF_INET6 == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num) { + sppe_udp_flow_add_ipv6(ct); + return 0; + } + + /* return fail */ + return (-1); +} +#endif + + /* Returns verdict for packet, and may modify conntracktype */ static int udp_packet(struct nf_conn *ct, const struct sk_buff *skb, @@ -77,7 +196,15 @@ static int udp_packet(struct nf_conn *ct nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout_stream); /* Also, more likely to be important, and not a probe */ if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) +#if defined (CONFIG_CNS3XXX_SPPE) + { +#endif nf_conntrack_event_cache(IPCT_STATUS, ct); +#if defined (CONFIG_CNS3XXX_SPPE) + /* Add SPPE hardware flow */ + sppe_udp_flow_add(ct); + } +#endif } else nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout);