| /******************************************************************************* |
| Copyright (C) Marvell International Ltd. and its affiliates |
| |
| This software file (the "File") is owned and distributed by Marvell |
| International Ltd. and/or its affiliates ("Marvell") under the following |
| alternative licensing terms. Once you have made an election to distribute the |
| File under one of the following license alternatives, please (i) delete this |
| introductory statement regarding license alternatives, (ii) delete the two |
| license alternatives that you have not elected to use and (iii) preserve the |
| Marvell copyright notice above. |
| |
| |
| ******************************************************************************** |
| Marvell GPL License Option |
| |
| If you received this File from Marvell, you may opt to use, redistribute and/or |
| modify this File in accordance with the terms and conditions of the General |
| Public License Version 2, June 1991 (the "GPL License"), a copy of which is |
| available along with the File in the license.txt file or by writing to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or |
| on the worldwide web at http://www.gnu.org/licenses/gpl.txt. |
| |
| THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY |
| DISCLAIMED. The GPL License provides additional details about this warranty |
| disclaimer. |
| *******************************************************************************/ |
| |
| #include "mvCommon.h" /* Should be included before mvSysHwConfig */ |
| #include <linux/kernel.h> |
| #include <linux/version.h> |
| #include <linux/netdevice.h> |
| #include <linux/etherdevice.h> |
| #include <linux/skbuff.h> |
| #include <linux/pci.h> |
| #include <linux/ip.h> |
| #include <linux/in.h> |
| #include <linux/tcp.h> |
| #include <linux/version.h> |
| #include <net/ip.h> |
| #include <net/xfrm.h> |
| |
| #include "mvCommon.h" |
| #include "mvOs.h" |
| #include "dbg-trace.h" |
| #include "mvSysHwConfig.h" |
| #include "eth/mvEth.h" |
| #include "eth/gbe/mvEthGbe.h" |
| #include "eth-phy/mvEthPhy.h" |
| #include "mvSysEthApi.h" |
| #include "msApi.h" |
| #include "boardEnv/mvBoardEnvLib.h" |
| |
| #include "mv_netdev.h" |
| |
| int SWITCH_PORT_CPU; |
| int SWITCH_PORT_0; |
| int SWITCH_PORT_1; |
| int SWITCH_PORT_2; |
| int SWITCH_PORT_3; |
| int SWITCH_PORT_4; |
| |
| |
| /* use this MACRO to find if a certain port (0-7) is actually connected */ |
| #define SWITCH_IS_PORT_CONNECTED(p) \ |
| ( ((p) == SWITCH_PORT_CPU) || ((p) == SWITCH_PORT_0) || \ |
| ((p) == SWITCH_PORT_1) || ((p) == SWITCH_PORT_2) || \ |
| ((p) == SWITCH_PORT_3) || ((p) == SWITCH_PORT_4) ) |
| |
| /* helpers for VLAN tag handling */ |
| #define MV_GTW_PORT_VLAN_ID(grp,port) ((grp)+(port)+1) |
| #define MV_GTW_GROUP_VLAN_ID(grp) (((grp)+1)<<8) |
| #define MV_GTW_VLANID_TO_PORT(vlanid) (((vlanid) & 0xf)-1) |
| |
| unsigned int switch_enabled_ports; |
| |
| #ifdef CONFIG_MV_GTW_LINK_STATUS |
| static int switch_irq = -1; |
| struct timer_list switch_link_timer; |
| #endif |
| |
| #ifdef CONFIG_MV_GTW_IGMP |
| extern int mv_gtw_igmp_snoop_init(void); |
| extern int mv_gtw_igmp_snoop_exit(void); |
| extern int mv_gtw_igmp_snoop_process(struct sk_buff* skb, unsigned char port, unsigned char vlan_dbnum); |
| #endif |
| |
| /* Example: "mv_net_config=(eth0,00:99:88:88:99:77,0)(eth1,00:55:44:55:66:77,1:2:3:4),mtu=1500" */ |
| static char *cmdline = NULL; |
| |
| struct mv_gtw_config gtw_config; |
| |
| GT_QD_DEV qddev, *qd_dev = NULL; |
| static GT_SYS_CONFIG qd_cfg; |
| |
| static int mv_gtw_port2lport(int port) |
| { |
| if(port==SWITCH_PORT_0) return 0; |
| if(port==SWITCH_PORT_1) return 1; |
| if(port==SWITCH_PORT_2) return 2; |
| if(port==SWITCH_PORT_3) return 3; |
| if(port==SWITCH_PORT_4) return 4; |
| return -1; |
| } |
| |
| /* Local function prototypes */ |
| |
| /* Required to get the configuration string from the Kernel Command Line */ |
| int mv_gtw_cmdline_config(char *s); |
| __setup("mv_net_config=", mv_gtw_cmdline_config); |
| |
| int mv_gtw_cmdline_config(char *s) |
| { |
| cmdline = s; |
| return 1; |
| } |
| |
| static int mv_gtw_check_open_bracket(char **p_net_config) |
| { |
| if (**p_net_config == '(') { |
| (*p_net_config)++; |
| return 0; |
| } |
| printk("Syntax error: could not find opening bracket\n"); |
| return -EINVAL; |
| } |
| |
| static int mv_gtw_check_closing_bracket(char **p_net_config) |
| { |
| if (**p_net_config == ')') { |
| (*p_net_config)++; |
| return 0; |
| } |
| printk("Syntax error: could not find closing bracket\n"); |
| return -EINVAL; |
| } |
| |
| static int mv_gtw_check_comma(char **p_net_config) |
| { |
| if (**p_net_config == ',') { |
| (*p_net_config)++; |
| return 0; |
| } |
| printk("Syntax error: could not find comma\n"); |
| return -EINVAL; |
| } |
| |
| |
| static int mv_gtw_is_digit(char ch) |
| { |
| if( ((ch >= '0') && (ch <= '9')) || |
| ((ch >= 'a') && (ch <= 'f')) || |
| ((ch >= 'A') && (ch <= 'F')) ) |
| return 0; |
| |
| return -1; |
| } |
| |
| static int mv_gtw_get_cmdline_mac_addr(char **p_net_config, int idx) |
| { |
| /* the MAC address should look like: 00:99:88:88:99:77 */ |
| /* that is, 6 two-digit numbers, separated by : */ |
| /* 6 times two-digits, plus 5 colons, total: 17 characters */ |
| const int exact_len = 17; |
| int i = 0; |
| int syntax_err = 0; |
| char *p_mac_addr = *p_net_config; |
| |
| /* check first 15 characters in groups of 3 characters at a time */ |
| for (i = 0; i < exact_len-2; i+=3) |
| { |
| if ( (mv_gtw_is_digit(**p_net_config) == 0) && |
| (mv_gtw_is_digit(*(*p_net_config+1)) == 0) && |
| ((*(*p_net_config+2)) == ':') ) |
| { |
| (*p_net_config) += 3; |
| } |
| else { |
| syntax_err = 1; |
| break; |
| } |
| } |
| |
| /* two characters remaining, must be two digits */ |
| if ( (mv_gtw_is_digit(**p_net_config) == 0) && |
| (mv_gtw_is_digit(*(*p_net_config+1)) == 0) ) |
| { |
| (*p_net_config) += 2; |
| } |
| else |
| syntax_err = 1; |
| |
| if (syntax_err == 0) { |
| mvMacStrToHex(p_mac_addr, gtw_config.vlan_cfg[idx].macaddr); |
| return 0; |
| } |
| printk("Syntax error while parsing MAC address from command line\n"); |
| return -EINVAL; |
| } |
| |
| static void mv_gtw_update_curr_port_mask(char digit, unsigned int *curr_port_mask) |
| { |
| if (digit == '0') |
| *curr_port_mask |= (1<<SWITCH_PORT_0); |
| if (digit == '1') |
| *curr_port_mask |= (1<<SWITCH_PORT_1); |
| if (digit == '2') |
| *curr_port_mask |= (1<<SWITCH_PORT_2); |
| if (digit == '3') |
| *curr_port_mask |= (1<<SWITCH_PORT_3); |
| if (digit == '4') |
| *curr_port_mask |= (1<<SWITCH_PORT_4); |
| } |
| |
| static int mv_gtw_get_port_mask(char **p_net_config, int idx) |
| { |
| /* the port mask should look like this: */ |
| /* example 1: 0 */ |
| /* example 2: 1:2:3:4 */ |
| /* that is, one or more one-digit numbers, separated with : */ |
| /* we have up to GTW_MAX_NUM_OF_IFS interfaces */ |
| |
| unsigned int curr_port_mask = 0, i = 0; |
| int syntax_err = 0; |
| |
| for (i = 0; i < GTW_MAX_NUM_OF_IFS; i++) |
| { |
| if (mv_gtw_is_digit(**p_net_config) == 0) |
| { |
| if (*(*p_net_config+1) == ':') |
| { |
| mv_gtw_update_curr_port_mask(**p_net_config, &curr_port_mask); |
| (*p_net_config) += 2; |
| } |
| else if (*(*p_net_config+1) == ')') |
| { |
| mv_gtw_update_curr_port_mask(**p_net_config, &curr_port_mask); |
| (*p_net_config)++; |
| break; |
| } |
| else { |
| syntax_err = 1; |
| break; |
| } |
| } |
| else { |
| syntax_err = 1; |
| break; |
| } |
| } |
| |
| if (syntax_err == 0) { |
| gtw_config.vlan_cfg[idx].ports_mask = curr_port_mask; |
| return 0; |
| } |
| printk("Syntax error while parsing port mask from command line\n"); |
| return -EINVAL; |
| } |
| |
| static int mv_gtw_get_mtu(char **p_net_config) |
| { |
| /* the mtu value is constructed as follows: */ |
| /* mtu=value */ |
| unsigned int mtu; |
| int syntax_err = 0; |
| |
| if(strncmp(*p_net_config,"mtu=",4) == 0) |
| { |
| *p_net_config += 4; |
| mtu = 0; |
| while((**p_net_config >= '0') && (**p_net_config <= '9')) |
| { |
| mtu = (mtu * 10) + (**p_net_config - '0'); |
| *p_net_config += 1; |
| } |
| if(**p_net_config != '\0') |
| syntax_err = 1; |
| } |
| else |
| { |
| syntax_err = 1; |
| } |
| |
| if(syntax_err == 0) |
| { |
| gtw_config.mtu = mtu; |
| printk(" o MTU set to %d.\n", mtu); |
| return 0; |
| } |
| |
| printk("Syntax error while parsing mtu value from command line\n"); |
| return -EINVAL; |
| } |
| |
| static int mv_gtw_parse_net_config(char* cmdline) |
| { |
| char *p_net_config = cmdline; |
| int i = 0; |
| int status = 0; |
| |
| if (p_net_config == NULL) |
| return -EINVAL; |
| |
| for (i=0; (i<GTW_MAX_NUM_OF_IFS) && (*p_net_config != '\0'); i++) |
| { |
| status = mv_gtw_check_open_bracket(&p_net_config); |
| if (status != 0) |
| break; |
| status = mv_gtw_get_cmdline_mac_addr(&p_net_config, i); |
| if (status != 0) |
| break; |
| status = mv_gtw_check_comma(&p_net_config); |
| if (status != 0) |
| break; |
| status = mv_gtw_get_port_mask(&p_net_config, i); |
| if (status != 0) |
| break; |
| status = mv_gtw_check_closing_bracket(&p_net_config); |
| if (status != 0) |
| break; |
| |
| gtw_config.vlans_num++; |
| |
| /* If we have a comma after the closing bracket, then interface */ |
| /* definition is done. */ |
| if(*p_net_config == ',') |
| break; |
| } |
| |
| if(*p_net_config != '\0') |
| { |
| status = mv_gtw_check_comma(&p_net_config); |
| if (status == 0) |
| { |
| status = mv_gtw_get_mtu(&p_net_config); |
| } |
| } |
| else |
| { |
| gtw_config.mtu = 1500; |
| printk(" o Using default MTU %d\n", gtw_config.mtu); |
| } |
| |
| /* at this point, we have parsed up to GTW_MAX_NUM_OF_IFS, and the mtu value */ |
| /* if the net_config string is not finished yet, then its format is invalid */ |
| if (*p_net_config != '\0') |
| { |
| printk("Gateway config string is too long: %s\n", p_net_config); |
| status = -EINVAL; |
| } |
| return status; |
| } |
| |
| GT_BOOL gtwReadMiiWrap(GT_QD_DEV* dev, unsigned int portNumber, unsigned int MIIReg, unsigned int* value) |
| { |
| unsigned long flags; |
| unsigned short tmp; |
| MV_STATUS status; |
| |
| spin_lock_irqsave(&mii_lock, flags); |
| |
| status = mvEthPhyRegRead(portNumber, MIIReg , &tmp); |
| spin_unlock_irqrestore(&mii_lock, flags); |
| *value = tmp; |
| |
| if (status == MV_OK) |
| return GT_TRUE; |
| |
| return GT_FALSE; |
| } |
| |
| |
| GT_BOOL gtwWriteMiiWrap(GT_QD_DEV* dev, unsigned int portNumber, unsigned int MIIReg, unsigned int data) |
| { |
| unsigned long flags; |
| unsigned short tmp; |
| MV_STATUS status; |
| |
| spin_lock_irqsave(&mii_lock, flags); |
| tmp = (unsigned short)data; |
| status = mvEthPhyRegWrite(portNumber, MIIReg, tmp); |
| |
| spin_unlock_irqrestore(&mii_lock, flags); |
| |
| if (status == MV_OK) |
| return GT_TRUE; |
| |
| return GT_FALSE; |
| } |
| |
| static int mv_gtw_set_port_based_vlan(unsigned int ports_mask) |
| { |
| unsigned int p, pl; |
| unsigned char cnt; |
| GT_LPORT port_list[MAX_SWITCH_PORTS]; |
| |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if( MV_BIT_CHECK(ports_mask, p) && (p != SWITCH_PORT_CPU) ) { |
| ETH_DBG( ETH_DBG_LOAD|ETH_DBG_MCAST|ETH_DBG_VLAN, ("port based vlan, port %d: ",p)); |
| for(pl=0,cnt=0; pl<qd_dev->numOfPorts; pl++) { |
| if( MV_BIT_CHECK(ports_mask, pl) && (pl != p) ) { |
| ETH_DBG( ETH_DBG_LOAD|ETH_DBG_MCAST|ETH_DBG_VLAN, ("%d ",pl)); |
| port_list[cnt] = pl; |
| cnt++; |
| } |
| } |
| if( gvlnSetPortVlanPorts(qd_dev, p, port_list, cnt) != GT_OK) { |
| printk("gvlnSetPortVlanPorts failed\n"); |
| return -1; |
| } |
| ETH_DBG( ETH_DBG_LOAD|ETH_DBG_MCAST|ETH_DBG_VLAN, ("\n")); |
| } |
| } |
| return 0; |
| } |
| |
| |
| static int mv_gtw_set_vlan_in_vtu(unsigned short vlan_id,unsigned int ports_mask) |
| { |
| GT_VTU_ENTRY vtu_entry; |
| unsigned int p; |
| |
| vtu_entry.vid = vlan_id; |
| vtu_entry.DBNum = MV_GTW_VLAN_TO_GROUP(vlan_id); |
| vtu_entry.vidPriOverride = GT_FALSE; |
| vtu_entry.vidPriority = 0; |
| vtu_entry.vidExInfo.useVIDFPri = GT_FALSE; |
| vtu_entry.vidExInfo.vidFPri = 0; |
| vtu_entry.vidExInfo.useVIDQPri = GT_FALSE; |
| vtu_entry.vidExInfo.vidQPri = 0; |
| vtu_entry.vidExInfo.vidNRateLimit = GT_FALSE; |
| ETH_DBG( ETH_DBG_LOAD|ETH_DBG_MCAST|ETH_DBG_VLAN, ("vtu entry: vid=0x%x, port ", vtu_entry.vid)); |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if(MV_BIT_CHECK(ports_mask, p)) { |
| ETH_DBG( ETH_DBG_LOAD|ETH_DBG_MCAST|ETH_DBG_VLAN, ("%d ", p)); |
| if(qd_dev->deviceId == GT_88E6061) { |
| /* for 6061 device, no double/provider tag controlling on ingress. */ |
| /* therefore, we need to strip the tag on egress on all ports except cpu port */ |
| /* anyway, if we're using header mode no vlan-tag need to be added here */ |
| vtu_entry.vtuData.memberTagP[p] = MEMBER_EGRESS_UNMODIFIED; |
| } |
| else { |
| vtu_entry.vtuData.memberTagP[p] = MEMBER_EGRESS_UNMODIFIED; |
| } |
| } |
| else { |
| vtu_entry.vtuData.memberTagP[p] = NOT_A_MEMBER; |
| } |
| vtu_entry.vtuData.portStateP[p] = 0; |
| } |
| if(gvtuAddEntry(qd_dev, &vtu_entry) != GT_OK) { |
| printk("gvtuAddEntry failed\n"); |
| return -1; |
| } |
| |
| ETH_DBG( ETH_DBG_LOAD|ETH_DBG_MCAST|ETH_DBG_VLAN, ("\n")); |
| return 0; |
| } |
| |
| int mv_gtw_set_mac_addr_to_switch(unsigned char *mac_addr, unsigned char db, unsigned int ports_mask, unsigned char op) |
| { |
| GT_ATU_ENTRY mac_entry; |
| struct mv_vlan_cfg *nc; |
| |
| /* validate db with VLAN id */ |
| nc = >w_config.vlan_cfg[db]; |
| if(MV_GTW_VLAN_TO_GROUP(nc->vlan_grp_id) != db) { |
| printk("mv_gtw_set_mac_addr_to_switch (invalid db)\n"); |
| return -1; |
| } |
| |
| memset(&mac_entry,0,sizeof(GT_ATU_ENTRY)); |
| |
| mac_entry.trunkMember = GT_FALSE; |
| mac_entry.prio = 0; |
| mac_entry.exPrio.useMacFPri = GT_FALSE; |
| mac_entry.exPrio.macFPri = 0; |
| mac_entry.exPrio.macQPri = 0; |
| mac_entry.DBNum = db; |
| mac_entry.portVec = ports_mask; |
| memcpy(mac_entry.macAddr.arEther,mac_addr,6); |
| |
| if(is_multicast_ether_addr(mac_addr)) |
| mac_entry.entryState.mcEntryState = GT_MC_STATIC; |
| else |
| mac_entry.entryState.ucEntryState = GT_UC_NO_PRI_STATIC; |
| |
| ETH_DBG(ETH_DBG_ALL, ("mv_gateway: db%d port-mask=0x%x, %02x:%02x:%02x:%02x:%02x:%02x ", |
| db, (unsigned int)mac_entry.portVec, |
| mac_entry.macAddr.arEther[0],mac_entry.macAddr.arEther[1],mac_entry.macAddr.arEther[2], |
| mac_entry.macAddr.arEther[3],mac_entry.macAddr.arEther[4],mac_entry.macAddr.arEther[5])); |
| |
| if((op == 0) || (mac_entry.portVec == 0)) { |
| if(gfdbDelAtuEntry(qd_dev, &mac_entry) != GT_OK) { |
| printk("gfdbDelAtuEntry failed\n"); |
| return -1; |
| } |
| ETH_DBG(ETH_DBG_ALL, ("deleted\n")); |
| } |
| else { |
| if(gfdbAddMacEntry(qd_dev, &mac_entry) != GT_OK) { |
| printk("gfdbAddMacEntry failed\n"); |
| return -1; |
| } |
| ETH_DBG(ETH_DBG_ALL, ("added\n")); |
| } |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_MV_GTW_IGMP |
| int mv_gtw_enable_igmp(void) |
| { |
| unsigned char p; |
| |
| ETH_DBG( ETH_DBG_IGMP, ("enabling L2 IGMP snooping\n")); |
| |
| /* enable IGMP snoop on all ports (except cpu port) */ |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if(p != SWITCH_PORT_CPU) { |
| if(gprtSetIGMPSnoop(qd_dev, p, GT_TRUE) != GT_OK) { |
| printk("gprtSetIGMPSnoop failed\n"); |
| return -1; |
| } |
| } |
| } |
| return -1; |
| } |
| #endif /* CONFIG_MV_GTW_IGMP */ |
| |
| |
| int __init mv_gtw_net_setup(int port) |
| { |
| struct mv_vlan_cfg *nc; |
| int i = 0; |
| |
| SWITCH_PORT_CPU = mvBoardSwitchCpuPortGet(port); |
| SWITCH_PORT_0 = mvBoardSwitchPortGet(port, 0); |
| SWITCH_PORT_1 = mvBoardSwitchPortGet(port, 1); |
| SWITCH_PORT_2 = mvBoardSwitchPortGet(port, 2); |
| SWITCH_PORT_3 = mvBoardSwitchPortGet(port, 3); |
| SWITCH_PORT_4 = mvBoardSwitchPortGet(port, 4); |
| |
| #ifdef CONFIG_MV_GTW_LINK_STATUS |
| switch_irq = mvBoardLinkStatusIrqGet(port); |
| if(switch_irq != -1) |
| switch_irq += IRQ_GPP_START; |
| #endif |
| |
| /* build the net config table */ |
| memset(>w_config, 0, sizeof(struct mv_gtw_config)); |
| |
| if(cmdline != NULL) |
| { |
| printk(" o Using command line network interface configuration\n"); |
| } |
| else |
| { |
| printk(" o Using default network configuration, overriding boot MAC address\n"); |
| cmdline = CONFIG_MV_GTW_CONFIG; |
| } |
| |
| if (mv_gtw_parse_net_config(cmdline) < 0) |
| { |
| printk("Error parsing mv_net_config\n"); |
| return -EINVAL; |
| } |
| |
| /* CPU port should always be enabled */ |
| switch_enabled_ports = (1 << SWITCH_PORT_CPU); |
| |
| for(i=0, nc=>w_config.vlan_cfg[i]; i<gtw_config.vlans_num; i++, nc++) |
| { |
| /* VLAN ID */ |
| nc->vlan_grp_id = MV_GTW_GROUP_VLAN_ID(i); |
| nc->ports_link = 0; |
| nc->header = cpu_to_be16( (MV_GTW_VLAN_TO_GROUP(nc->vlan_grp_id) << 12) |
| | nc->ports_mask); |
| /* print info */ |
| printk(" o mac_addr %02x:%02x:%02x:%02x:%02x:%02x, VID 0x%03x, port list: ", |
| nc->macaddr[0], nc->macaddr[1], nc->macaddr[2], |
| nc->macaddr[3], nc->macaddr[4], nc->macaddr[5], nc->vlan_grp_id); |
| |
| if(nc->ports_mask & (1<<SWITCH_PORT_CPU)) |
| printk("port-CPU "); |
| if(nc->ports_mask & (1<<SWITCH_PORT_0)) |
| printk("port-0 "); |
| if(nc->ports_mask & (1<<SWITCH_PORT_1)) |
| printk("port-1 "); |
| if(nc->ports_mask & (1<<SWITCH_PORT_2)) |
| printk("port-2 "); |
| if(nc->ports_mask & (1<<SWITCH_PORT_3)) |
| printk("port-3 "); |
| if(nc->ports_mask & (1<<SWITCH_PORT_4)) |
| printk("port-4 "); |
| printk("\n"); |
| |
| /* collect per-interface port_mask into a global port_mask, used for enabling the Switch ports */ |
| switch_enabled_ports |= nc->ports_mask; |
| } |
| |
| return 0; |
| } |
| |
| static int mv_switch_init(int port) |
| { |
| unsigned int i, p; |
| unsigned char cnt; |
| GT_LPORT port_list[MAX_SWITCH_PORTS]; |
| struct mv_vlan_cfg *nc; |
| GT_JUMBO_MODE jumbo_mode; |
| |
| /* printk("init switch layer... "); */ |
| |
| memset((char*)&qd_cfg,0,sizeof(GT_SYS_CONFIG)); |
| |
| /* init config structure for qd package */ |
| qd_cfg.BSPFunctions.readMii = gtwReadMiiWrap; |
| qd_cfg.BSPFunctions.writeMii = gtwWriteMiiWrap; |
| qd_cfg.BSPFunctions.semCreate = NULL; |
| qd_cfg.BSPFunctions.semDelete = NULL; |
| qd_cfg.BSPFunctions.semTake = NULL; |
| qd_cfg.BSPFunctions.semGive = NULL; |
| qd_cfg.initPorts = GT_TRUE; |
| qd_cfg.cpuPortNum = SWITCH_PORT_CPU; |
| if (mvBoardSmiScanModeGet(port) == 1) { |
| qd_cfg.mode.baseAddr = 0; |
| qd_cfg.mode.scanMode = SMI_MANUAL_MODE; |
| } |
| else if (mvBoardSmiScanModeGet(port) == 2) { |
| qd_cfg.mode.baseAddr = 0xA; |
| qd_cfg.mode.scanMode = SMI_MULTI_ADDR_MODE; |
| } |
| /* load switch sw package */ |
| if( qdLoadDriver(&qd_cfg, &qddev) != GT_OK) { |
| printk("qdLoadDriver failed\n"); |
| return -1; |
| } |
| qd_dev = &qddev; |
| |
| ETH_DBG( ETH_DBG_LOAD, ("Device ID : 0x%x\n",qd_dev->deviceId)); |
| ETH_DBG( ETH_DBG_LOAD, ("Base Reg Addr : 0x%x\n",qd_dev->baseRegAddr)); |
| ETH_DBG( ETH_DBG_LOAD, ("No. of Ports : %d\n",qd_dev->numOfPorts)); |
| ETH_DBG( ETH_DBG_LOAD, ("CPU Ports : %ld\n",qd_dev->cpuPortNum)); |
| |
| /* disable all ports */ |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| gstpSetPortState(qd_dev, p, GT_PORT_DISABLE); |
| } |
| |
| /* initialize Switch according to Switch ID */ |
| switch (qd_dev->deviceId) |
| { |
| case GT_88E6131: |
| case GT_88E6108: |
| /* enable external ports */ |
| ETH_DBG( ETH_DBG_LOAD, ("enable phy polling for external ports\n")); |
| if(gsysSetPPUEn(qd_dev, GT_TRUE) != GT_OK) { |
| printk("gsysSetPPUEn failed\n"); |
| return -1; |
| } |
| /* Note: The GbE unit in SoCs connected to these switches does not support Marvell Header Mode */ |
| /* so we always use VLAN tags here */ |
| /* set cpu-port with ingress double-tag mode */ |
| ETH_DBG( ETH_DBG_LOAD, ("cpu port ingress double-tag mode\n")); |
| if(gprtSetDoubleTag(qd_dev, SWITCH_PORT_CPU, GT_TRUE) != GT_OK) { |
| printk("gprtSetDoubleTag failed\n"); |
| return -1; |
| } |
| /* set cpu-port with egrees add-tag mode */ |
| ETH_DBG( ETH_DBG_LOAD, ("cpu port egrees add-tag mode\n")); |
| if(gprtSetEgressMode(qd_dev, SWITCH_PORT_CPU, GT_ADD_TAG) != GT_OK) { |
| printk("gprtSetEgressMode failed\n"); |
| return -1; |
| } |
| /* config the switch to use the double tag data (relevant to cpu-port only) */ |
| ETH_DBG( ETH_DBG_LOAD, ("use double-tag and remove\n")); |
| if(gsysSetUseDoubleTagData(qd_dev,GT_TRUE) != GT_OK) { |
| printk("gsysSetUseDoubleTagData failed\n"); |
| return -1; |
| } |
| /* set cpu-port with 802.1q secured mode */ |
| ETH_DBG( ETH_DBG_LOAD, ("cpu port-based 802.1q secure mode\n")); |
| if(gvlnSetPortVlanDot1qMode(qd_dev,SWITCH_PORT_CPU,GT_SECURE) != GT_OK) { |
| printk("gvlnSetPortVlanDot1qMode failed\n"); |
| return -1; |
| } |
| break; |
| |
| case GT_88E6065: |
| /* set CPU port number */ |
| if(gsysSetCPUPort(qd_dev, SWITCH_PORT_CPU) != GT_OK) { |
| printk("gsysSetCPUPort failed\n"); |
| return -1; |
| } |
| |
| if(gstatsFlushAll(qd_dev) != GT_OK) |
| printk("gstatsFlushAll failed\n"); |
| |
| /* set all ports not to unmodify the vlan tag on egress */ |
| for(i=0; i<qd_dev->numOfPorts; i++) |
| { |
| if(gprtSetEgressMode(qd_dev, i, GT_UNMODIFY_EGRESS) != GT_OK) { |
| printk("gprtSetEgressMode GT_UNMODIFY_EGRESS failed\n"); |
| return -1; |
| } |
| } |
| if(gprtSetHeaderMode(qd_dev, SWITCH_PORT_CPU, GT_TRUE) != GT_OK) { |
| printk("gprtSetHeaderMode GT_TRUE failed\n"); |
| return -1; |
| } |
| |
| /* init counters */ |
| if(gprtClearAllCtr(qd_dev) != GT_OK) |
| printk("gprtClearAllCtr failed\n"); |
| if(gprtSetCtrMode(qd_dev, GT_CTR_ALL) != GT_OK) |
| printk("gprtSetCtrMode failed\n"); |
| |
| break; |
| |
| case GT_88E6061: |
| /* set CPU port number */ |
| if(gsysSetCPUPort(qd_dev, SWITCH_PORT_CPU) != GT_OK) { |
| printk("gsysSetCPUPort failed\n"); |
| return -1; |
| } |
| |
| /* set all ports not to unmodify the vlan tag on egress */ |
| for(i=0; i<qd_dev->numOfPorts; i++) { |
| if(gprtSetEgressMode(qd_dev, i, GT_UNMODIFY_EGRESS) != GT_OK) { |
| printk("gprtSetEgressMode GT_UNMODIFY_EGRESS failed\n"); |
| return -1; |
| } |
| } |
| |
| if(gprtSetHeaderMode(qd_dev,SWITCH_PORT_CPU,GT_TRUE) != GT_OK) { |
| printk("gprtSetHeaderMode GT_TRUE failed\n"); |
| return -1; |
| } |
| |
| /* init counters */ |
| if(gprtClearAllCtr(qd_dev) != GT_OK) |
| printk("gprtClearAllCtr failed\n"); |
| if(gprtSetCtrMode(qd_dev, GT_CTR_ALL) != GT_OK) |
| printk("gprtSetCtrMode failed\n"); |
| |
| break; |
| |
| case GT_88E6161: |
| case GT_88E6165: |
| if(gstatsFlushAll(qd_dev) != GT_OK) { |
| printk("gstatsFlushAll failed\n"); |
| } |
| |
| /* set all ports not to unmodify the vlan tag on egress */ |
| for(i=0; i<qd_dev->numOfPorts; i++) { |
| if(gprtSetEgressMode(qd_dev, i, GT_UNMODIFY_EGRESS) != GT_OK) { |
| printk("gprtSetEgressMode GT_UNMODIFY_EGRESS failed\n"); |
| return -1; |
| } |
| } |
| if(gprtSetHeaderMode(qd_dev,SWITCH_PORT_CPU,GT_TRUE) != GT_OK) { |
| printk("gprtSetHeaderMode GT_TRUE failed\n"); |
| return -1; |
| } |
| |
| /* Setup jumbo frames mode. */ |
| if( MV_RX_BUF_SIZE(gtw_config.mtu) <= 1522) |
| jumbo_mode = GT_JUMBO_MODE_1522; |
| else if( MV_RX_BUF_SIZE(gtw_config.mtu) <= 2048) |
| jumbo_mode = GT_JUMBO_MODE_2048; |
| else |
| jumbo_mode = GT_JUMBO_MODE_10240; |
| |
| for(i=0; i<qd_dev->numOfPorts; i++) { |
| if(gsysSetJumboMode(qd_dev, i, jumbo_mode) != GT_OK) { |
| printk("gsysSetJumboMode %d failed\n",jumbo_mode); |
| return -1; |
| } |
| } |
| break; |
| |
| default: |
| printk("Unsupported Switch. Switch ID is 0x%X.\n",qd_dev->deviceId); |
| return -1; |
| } |
| |
| /* set priorities rules */ |
| for(i=0; i<qd_dev->numOfPorts; i++) { |
| /* default port priority to queue zero */ |
| if(gcosSetPortDefaultTc(qd_dev, i, 0) != GT_OK) |
| printk("gcosSetPortDefaultTc failed (port %d)\n", i); |
| |
| /* enable IP TOS Prio */ |
| if(gqosIpPrioMapEn(qd_dev, i, GT_TRUE) != GT_OK) |
| printk("gqosIpPrioMapEn failed (port %d)\n",i); |
| |
| /* set IP QoS */ |
| if(gqosSetPrioMapRule(qd_dev, i, GT_FALSE) != GT_OK) |
| printk("gqosSetPrioMapRule failed (port %d)\n",i); |
| |
| /* disable Vlan QoS Prio */ |
| if(gqosUserPrioMapEn(qd_dev, i, GT_FALSE) != GT_OK) |
| printk("gqosUserPrioMapEn failed (port %d)\n",i); |
| |
| /* Set force flow control to FALSE for all ports */ |
| if(gprtSetForceFc(qd_dev, i, GT_FALSE) != GT_OK) |
| printk("gprtSetForceFc failed (port %d)\n",i); |
| } |
| |
| /* The switch CPU port is not part of the VLAN, but rather connected by tunneling to each */ |
| /* of the VLAN's ports. Our MAC addr will be added during start operation to the VLAN DB */ |
| /* at switch level to forward packets with this DA to CPU port. */ |
| ETH_DBG( ETH_DBG_LOAD, ("Enabling Tunneling on ports: ")); |
| for(i=0; i<qd_dev->numOfPorts; i++) { |
| if(i != SWITCH_PORT_CPU) { |
| if(gprtSetVlanTunnel(qd_dev, i, GT_TRUE) != GT_OK) { |
| printk("gprtSetVlanTunnel failed (port %d)\n",i); |
| return -1; |
| } |
| else { |
| ETH_DBG( ETH_DBG_LOAD, ("%d ",i)); |
| } |
| } |
| } |
| ETH_DBG( ETH_DBG_LOAD, ("\n")); |
| |
| /* configure ports (excluding CPU port) for each network interface (VLAN): */ |
| for(i=0, nc=>w_config.vlan_cfg[i]; i<gtw_config.vlans_num; i++,nc++) { |
| ETH_DBG( ETH_DBG_LOAD, ("vlan%d configuration (nc->ports_mask = 0x%08x) \n", |
| i, nc->ports_mask)); |
| /* set port's defaul private vlan id and database number (DB per group): */ |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if( MV_BIT_CHECK(nc->ports_mask, p) && (p != SWITCH_PORT_CPU) ) { |
| ETH_DBG(ETH_DBG_LOAD,("port %d default private vlan id: 0x%x\n", p, MV_GTW_PORT_VLAN_ID(nc->vlan_grp_id,p))); |
| if( gvlnSetPortVid(qd_dev, p, MV_GTW_PORT_VLAN_ID(nc->vlan_grp_id,p)) != GT_OK ) { |
| printk("gvlnSetPortVid failed"); |
| return -1; |
| } |
| if( gvlnSetPortVlanDBNum(qd_dev, p, MV_GTW_VLAN_TO_GROUP(nc->vlan_grp_id)) != GT_OK) { |
| printk("gvlnSetPortVlanDBNum failed\n"); |
| return -1; |
| } |
| } |
| } |
| |
| /* set port's port-based vlan (CPU port is not part of VLAN) */ |
| if(mv_gtw_set_port_based_vlan(nc->ports_mask & ~(1<<SWITCH_PORT_CPU)) != 0) { |
| printk("mv_gtw_set_port_based_vlan failed\n"); |
| } |
| |
| /* set vtu with group vlan id (used in tx) */ |
| if(mv_gtw_set_vlan_in_vtu(nc->vlan_grp_id, nc->ports_mask | (1<<SWITCH_PORT_CPU)) != 0) { |
| printk("mv_gtw_set_vlan_in_vtu failed\n"); |
| } |
| |
| /* set vtu with each port private vlan id (used in rx) */ |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if(MV_BIT_CHECK(nc->ports_mask, p) && (p!=SWITCH_PORT_CPU)) { |
| if(mv_gtw_set_vlan_in_vtu(MV_GTW_PORT_VLAN_ID(nc->vlan_grp_id,p), |
| nc->ports_mask & ~(1<<SWITCH_PORT_CPU)) != 0) { |
| printk("mv_gtw_set_vlan_in_vtu failed\n"); |
| } |
| } |
| } |
| } |
| |
| /* set cpu-port with port-based vlan to all other ports */ |
| ETH_DBG( ETH_DBG_LOAD, ("cpu port-based vlan:")); |
| for(p=0,cnt=0; p<qd_dev->numOfPorts; p++) { |
| if(p != SWITCH_PORT_CPU) { |
| ETH_DBG( ETH_DBG_LOAD, ("%d ",p)); |
| port_list[cnt] = p; |
| cnt++; |
| } |
| } |
| ETH_DBG( ETH_DBG_LOAD, ("\n")); |
| if( gvlnSetPortVlanPorts(qd_dev, SWITCH_PORT_CPU, port_list, cnt) != GT_OK) { |
| printk("gvlnSetPortVlanPorts failed\n"); |
| return -1; |
| } |
| |
| if(gfdbFlush(qd_dev,GT_FLUSH_ALL) != GT_OK) { |
| printk("gfdbFlush failed\n"); |
| } |
| |
| /* done! enable all Switch ports according to the net config table */ |
| ETH_DBG( ETH_DBG_LOAD, ("enabling: ports ")); |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if (MV_BIT_CHECK(switch_enabled_ports, p)) { |
| ETH_DBG( ETH_DBG_LOAD, ("%d ",p)); |
| if(gstpSetPortState(qd_dev, p, GT_PORT_FORWARDING) != GT_OK) { |
| printk("gstpSetPortState failed\n"); |
| } |
| } |
| } |
| ETH_DBG( ETH_DBG_LOAD, ("\n")); |
| |
| #ifdef CONFIG_MV_GTW_LINK_STATUS |
| /* Enable Phy Link Status Changed interrupt at Phy level for the all enabled ports */ |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if(MV_BIT_CHECK(switch_enabled_ports, p) && (p != SWITCH_PORT_CPU)) { |
| if(gprtPhyIntEnable(qd_dev, p, (GT_LINK_STATUS_CHANGED)) != GT_OK) { |
| printk("gprtPhyIntEnable failed port %d\n", p); |
| } |
| } |
| } |
| |
| if ((qd_dev->deviceId != GT_88E6161) && (qd_dev->deviceId != GT_88E6165)) { |
| if (switch_irq != -1) { |
| if(eventSetActive(qd_dev, GT_PHY_INTERRUPT) != GT_OK) { |
| printk("eventSetActive failed\n"); |
| } |
| } |
| } |
| else { |
| GT_DEV_EVENT gt_event = {GT_DEV_INT_PHY, 0, 0x1F}; /* 0x1F is a bit mask for ports 0-4 */ |
| if (switch_irq != -1) { |
| if(eventSetDevInt(qd_dev, >_event) != GT_OK) { |
| printk("eventSetDevInt failed\n"); |
| } |
| if(eventSetActive(qd_dev, GT_DEVICE_INT) != GT_OK) { |
| printk("eventSetActive failed\n"); |
| } |
| } |
| } |
| #endif /* CONFIG_MV_GTW_LINK_STATUS */ |
| |
| /* Configure Ethernet related LEDs, currently according to Switch ID */ |
| switch (qd_dev->deviceId) { |
| case GT_88E6131: |
| case GT_88E6108: |
| /* config LEDs: Bi-Color Mode-4: */ |
| /* 1000 Mbps Link - Solid Green; 1000 Mbps Activity - Blinking Green */ |
| /* 100 Mbps Link - Solid Red; 100 Mbps Activity - Blinking Red */ |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if( (p != SWITCH_PORT_CPU) && (SWITCH_IS_PORT_CONNECTED(p)) ) { |
| /* Configure Register 16 page 3 to 0x888F for mode 4 */ |
| if(gprtSetPagedPhyReg(qd_dev,p,16,3,0x888F)) { |
| printk("gprtSetPagedPhyReg failed (port=%d)\n", p); |
| } |
| /* Configure Register 17 page 3 to 0x4400 50% mixed LEDs */ |
| if(gprtSetPagedPhyReg(qd_dev,p,17,3,0x4400)) { |
| printk("gprtSetPagedPhyReg failed (port=%d)\n", p); |
| } |
| } |
| } |
| break; |
| |
| case GT_88E6161: |
| case GT_88E6165: |
| break; /* do nothing */ |
| |
| default: |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if( (p != SWITCH_PORT_CPU) && (SWITCH_IS_PORT_CONNECTED(p)) ) { |
| if(gprtSetPhyReg(qd_dev,p,22,0x1FFA)) { |
| /* Configure Register 22 LED0 to 0xA for Link/Act */ |
| printk("gprtSetPhyReg failed (port=%d)\n", p); |
| } |
| } |
| } |
| break; |
| } |
| |
| /* printk("done\n"); */ |
| |
| return 0; |
| } |
| |
| int mv_gtw_switch_tos_get(int port, unsigned char tos) |
| { |
| unsigned char queue; |
| int rc; |
| |
| rc = gcosGetDscp2Tc(qd_dev, tos>>2, &queue); |
| if(rc) |
| return -1; |
| |
| return (int)queue; |
| } |
| |
| int mv_gtw_switch_tos_set(int port, unsigned char tos, int queue) |
| { |
| return gcosSetDscp2Tc(qd_dev, tos>>2, (unsigned char)queue); |
| } |
| |
| static struct net_device* mv_gtw_main_net_dev_get(void) |
| { |
| int i; |
| mv_eth_priv *priv; |
| struct net_device *dev; |
| |
| for (i=0; i<mv_net_devs_num; i++) { |
| dev = mv_net_devs[i]; |
| priv = MV_ETH_PRIV(dev); |
| |
| if (netif_running(dev) && priv->isGtw) |
| return dev; |
| } |
| return NULL; |
| } |
| |
| int mv_gtw_set_mac_addr( struct net_device *dev, void *p ) |
| { |
| struct mv_vlan_cfg *vlan_cfg = MV_NETDEV_VLAN(dev); |
| struct sockaddr *addr = p; |
| |
| if(!is_valid_ether_addr(addr->sa_data)) |
| return -EADDRNOTAVAIL; |
| |
| /* remove old mac addr from VLAN DB */ |
| mv_gtw_set_mac_addr_to_switch(dev->dev_addr,MV_GTW_VLAN_TO_GROUP(vlan_cfg->vlan_grp_id),(1<<SWITCH_PORT_CPU),0); |
| |
| memcpy(dev->dev_addr, addr->sa_data, 6); |
| |
| /* add new mac addr to VLAN DB */ |
| mv_gtw_set_mac_addr_to_switch(dev->dev_addr,MV_GTW_VLAN_TO_GROUP(vlan_cfg->vlan_grp_id),(1<<SWITCH_PORT_CPU),1); |
| |
| printk("mv_gateway: %s change mac address to %02x:%02x:%02x:%02x:%02x:%02x\n", |
| dev->name, *(dev->dev_addr), *(dev->dev_addr+1), *(dev->dev_addr+2), |
| *(dev->dev_addr+3), *(dev->dev_addr+4), *(dev->dev_addr+5)); |
| |
| return 0; |
| } |
| |
| void mv_gtw_set_multicast_list(struct net_device *dev) |
| { |
| struct dev_mc_list *curr_addr = dev->mc_list; |
| struct mv_vlan_cfg *vlan_cfg = MV_NETDEV_VLAN(dev); |
| int i; |
| GT_ATU_ENTRY mac_entry; |
| GT_BOOL found = GT_FALSE; |
| GT_STATUS status; |
| |
| if((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI)) { |
| /* promiscuous mode - connect the CPU port to the VLAN (port based + 802.1q) */ |
| /* |
| if(dev->flags & IFF_PROMISC) |
| printk("mv_gateway: setting promiscuous mode\n"); |
| if(dev->flags & IFF_ALLMULTI) |
| printk("mv_gateway: setting multicast promiscuous mode\n"); |
| */ |
| mv_gtw_set_port_based_vlan(vlan_cfg->ports_mask|(1<<SWITCH_PORT_CPU)); |
| for(i=0; i<qd_dev->numOfPorts; i++) |
| { |
| if(MV_BIT_CHECK(vlan_cfg->ports_mask, i) && (i!=SWITCH_PORT_CPU)) |
| { |
| if(mv_gtw_set_vlan_in_vtu(MV_GTW_PORT_VLAN_ID(vlan_cfg->vlan_grp_id,i), |
| vlan_cfg->ports_mask | (1<<SWITCH_PORT_CPU)) != 0) |
| { |
| printk("mv_gtw_set_vlan_in_vtu failed\n"); |
| } |
| } |
| } |
| } |
| else |
| { |
| /* not in promiscuous or allmulti mode - disconnect the CPU port to the VLAN (port based + 802.1q) */ |
| mv_gtw_set_port_based_vlan(vlan_cfg->ports_mask&(~(1<<SWITCH_PORT_CPU))); |
| for(i=0; i<qd_dev->numOfPorts; i++) { |
| if(MV_BIT_CHECK(vlan_cfg->ports_mask, i) && (i!=SWITCH_PORT_CPU)) { |
| if(mv_gtw_set_vlan_in_vtu(MV_GTW_PORT_VLAN_ID(vlan_cfg->vlan_grp_id,i),vlan_cfg->ports_mask&(~(1<<SWITCH_PORT_CPU))) != 0) { |
| printk("mv_gtw_set_vlan_in_vtu failed\n"); |
| } |
| } |
| } |
| if(dev->mc_count) { |
| /* accept specific multicasts */ |
| for(i=0; i<dev->mc_count; i++, curr_addr = curr_addr->next) { |
| if (!curr_addr) |
| break; |
| /* The Switch may already have information about this multicast address in */ |
| /* its ATU. If this address is already in the ATU, use the existing port vector */ |
| /* ORed with the CPU port. Otherwise, just use the CPU port. */ |
| memset(&mac_entry,0,sizeof(GT_ATU_ENTRY)); |
| mac_entry.DBNum = MV_GTW_VLAN_TO_GROUP(vlan_cfg->vlan_grp_id); |
| memcpy(mac_entry.macAddr.arEther, curr_addr->dmi_addr, 6); |
| status = gfdbFindAtuMacEntry(qd_dev, &mac_entry, &found); |
| if ( (status != GT_OK) || (found != GT_TRUE) ) |
| { |
| mv_gtw_set_mac_addr_to_switch(curr_addr->dmi_addr, |
| MV_GTW_VLAN_TO_GROUP(vlan_cfg->vlan_grp_id), |
| (1<<SWITCH_PORT_CPU)|(vlan_cfg->ports_mask), 1); |
| } |
| else |
| { |
| mv_gtw_set_mac_addr_to_switch(curr_addr->dmi_addr, |
| MV_GTW_VLAN_TO_GROUP(vlan_cfg->vlan_grp_id), |
| (mac_entry.portVec | (1<<SWITCH_PORT_CPU)), 1); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| int mv_gtw_change_mtu(struct net_device *dev, int mtu) |
| { |
| printk("mv_gateway does not support changing MTU at runtime.\n"); |
| return -EPERM; |
| } |
| |
| |
| int mv_gtw_start( struct net_device *dev ) |
| { |
| mv_eth_priv *priv = MV_ETH_PRIV(dev); |
| struct mv_vlan_cfg *vlan_cfg = MV_NETDEV_VLAN(dev); |
| unsigned char broadcast[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; |
| |
| printk("mv_gateway: starting %s\n",dev->name); |
| |
| /* start upper layer */ |
| netif_carrier_on(dev); |
| netif_wake_queue(dev); |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) |
| netif_poll_enable(dev); |
| #else |
| if ( (priv->net_dev == dev) || !netif_running(priv->net_dev) ) |
| { |
| napi_enable(&priv->napi); |
| } |
| #endif |
| |
| /* Add our MAC addr to the VLAN DB at switch level to forward packets with this DA */ |
| /* to CPU port by using the tunneling feature. The device is always in promisc mode. */ |
| mv_gtw_set_mac_addr_to_switch(dev->dev_addr, MV_GTW_VLAN_TO_GROUP(vlan_cfg->vlan_grp_id), (1<<SWITCH_PORT_CPU), 1); |
| |
| /* We also need to allow L2 broadcasts comming up for this interface */ |
| mv_gtw_set_mac_addr_to_switch(broadcast, MV_GTW_VLAN_TO_GROUP(vlan_cfg->vlan_grp_id), |
| vlan_cfg->ports_mask|(1<<SWITCH_PORT_CPU), 1); |
| |
| if (priv->timer_flag == 0) |
| { |
| priv->timer.expires = jiffies + ((HZ*CONFIG_MV_ETH_TIMER_PERIOD)/1000); /*ms*/ |
| add_timer( &(priv->timer) ); |
| priv->timer_flag = 1; |
| } |
| |
| if ( (priv->net_dev == dev) || !netif_running(priv->net_dev) ) |
| { |
| priv->net_dev = dev; |
| |
| /* connect to MAC port interrupt line */ |
| if ( request_irq( ETH_PORT_IRQ_NUM(priv->port), mv_eth_interrupt_handler, |
| (IRQF_DISABLED | IRQF_SAMPLE_RANDOM), "mv_gateway", priv) ) |
| { |
| printk(KERN_ERR "failed to assign irq%d\n", ETH_PORT_IRQ_NUM(priv->port)); |
| } |
| |
| /* unmask interrupts */ |
| mv_eth_unmask_interrupts(priv); |
| } |
| |
| return 0; |
| } |
| |
| |
| int mv_gtw_stop( struct net_device *dev ) |
| { |
| mv_eth_priv *priv = MV_ETH_PRIV(dev); |
| struct mv_vlan_cfg *vlan_cfg = MV_NETDEV_VLAN(dev); |
| |
| printk("mv_gateway: stopping %s\n",dev->name); |
| |
| /* stop upper layer */ |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) |
| netif_poll_disable(dev); |
| #endif |
| netif_carrier_off(dev); |
| netif_stop_queue(dev); |
| |
| /* stop switch from forwarding packets from this VLAN toward CPU port */ |
| if( gfdbFlushInDB(qd_dev, GT_FLUSH_ALL, MV_GTW_VLAN_TO_GROUP(vlan_cfg->vlan_grp_id)) != GT_OK) { |
| printk("gfdbFlushInDB failed\n"); |
| } |
| |
| if(priv->net_dev == dev) |
| { |
| struct net_device *main_dev = mv_gtw_main_net_dev_get(); |
| |
| if(main_dev == NULL) |
| { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) |
| napi_disable(&priv->napi); |
| #endif |
| mv_eth_mask_interrupts(priv); |
| priv->timer_flag = 0; |
| del_timer(&priv->timer); |
| |
| free_irq( dev->irq, priv ); |
| } |
| else |
| { |
| priv->net_dev = main_dev; |
| } |
| } |
| return 0; |
| } |
| |
| #ifdef CONFIG_MV_GTW_LINK_STATUS |
| static void mv_gtw_update_link_status(unsigned int p, unsigned int link_up) |
| { |
| struct mv_vlan_cfg *vlan_cfg; |
| int i = 0; |
| unsigned int prev_ports_link = 0; |
| |
| for(i=0; i<mv_net_devs_num; i++) { |
| if (mv_net_devs[i] == NULL) |
| break; |
| vlan_cfg = MV_NETDEV_VLAN(mv_net_devs[i]); |
| if ( vlan_cfg != NULL) { |
| |
| if ((vlan_cfg->ports_mask & (1 << p)) == 0) |
| continue; |
| |
| prev_ports_link = vlan_cfg->ports_link; |
| |
| if (link_up) |
| vlan_cfg->ports_link |= (1 << p); |
| else |
| vlan_cfg->ports_link &= ~(1 << p); |
| |
| if ((vlan_cfg->ports_link & vlan_cfg->ports_mask) == 0) { |
| netif_carrier_off(mv_net_devs[i]); |
| netif_stop_queue(mv_net_devs[i]); |
| } |
| else if (prev_ports_link == 0) { |
| netif_carrier_on(mv_net_devs[i]); |
| netif_wake_queue(mv_net_devs[i]); |
| } |
| } |
| } |
| } |
| |
| static irqreturn_t mv_gtw_link_interrupt_handler(int irq , void *dev_id) |
| { |
| unsigned short switch_cause, phy_cause, phys_port, p; |
| |
| if (switch_irq != -1 ) { |
| if ( (qd_dev->deviceId == GT_88E6161) || (qd_dev->deviceId == GT_88E6165) ) { |
| OUT GT_DEV_INT_STATUS devIntStatus; |
| /* required to clear the interrupt, and updates phys_port */ |
| geventGetDevIntStatus(qd_dev, &devIntStatus); |
| phys_port = devIntStatus.phyInt & 0xFF; |
| if (phys_port) |
| switch_cause = GT_PHY_INTERRUPT; |
| } |
| else { |
| if(eventGetIntStatus(qd_dev, &switch_cause) != GT_OK) |
| switch_cause = 0; |
| } |
| } |
| else { |
| switch_cause = GT_PHY_INTERRUPT; |
| } |
| |
| if(switch_cause & GT_PHY_INTERRUPT) { |
| /* If we're using a 6161/6165 Switch and using the Switch interrupt, we already have phys_port updated above */ |
| /* If we're using any other Switch, or if we're using polling, we need to update phys_port now */ |
| if ( (qd_dev->deviceId == GT_88E6161) || (qd_dev->deviceId == GT_88E6165)) { |
| if (switch_irq == -1) { |
| gprtGetPhyIntPortSummary(qd_dev,&phys_port); |
| phys_port |= 0x18; /* we cannot get indication for these ports in this method, so check them */ |
| } |
| } |
| else { |
| /* not 6161 or 6165 */ |
| gprtGetPhyIntPortSummary(qd_dev,&phys_port); |
| } |
| |
| for(p=0; p<qd_dev->numOfPorts; p++) { |
| if (MV_BIT_CHECK(phys_port, p)) { |
| if(gprtGetPhyIntStatus(qd_dev,p,&phy_cause) == GT_OK) { |
| if(phy_cause & GT_LINK_STATUS_CHANGED) |
| { |
| char *link=NULL, *duplex=NULL, *speed=NULL; |
| GT_BOOL flag; |
| GT_PORT_SPEED_MODE speed_mode; |
| |
| if(gprtGetLinkState(qd_dev,p,&flag) != GT_OK) { |
| printk("gprtGetLinkState failed (port %d)\n",p); |
| link = "ERR"; |
| } |
| else |
| link = (flag)?"up":"down"; |
| |
| if(flag) { |
| if(gprtGetDuplex(qd_dev,p,&flag) != GT_OK) { |
| printk("gprtGetDuplex failed (port %d)\n",p); |
| duplex = "ERR"; |
| } |
| else |
| duplex = (flag)?"Full":"Half"; |
| |
| if(gprtGetSpeedMode(qd_dev,p,&speed_mode) != GT_OK) { |
| printk("gprtGetSpeedMode failed (port %d)\n",p); |
| speed = "ERR"; |
| } |
| else { |
| if (speed_mode == PORT_SPEED_1000_MBPS) |
| speed = "1000Mbps"; |
| else if (speed_mode == PORT_SPEED_100_MBPS) |
| speed = "100Mbps"; |
| else |
| speed = "10Mbps"; |
| } |
| |
| mv_gtw_update_link_status(p, 1); |
| |
| printk("Port %d: Link-%s, %s-duplex, Speed-%s.\n", |
| mv_gtw_port2lport(p),link,duplex,speed); |
| } |
| else { |
| mv_gtw_update_link_status(p, 0); |
| |
| printk("Port %d: Link-down\n",mv_gtw_port2lport(p)); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (switch_irq == -1 ) { |
| switch_link_timer.expires = jiffies + (HZ); /* 1 second */ |
| add_timer(&switch_link_timer); |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void mv_gtw_link_timer_function(unsigned long data) |
| { |
| mv_gtw_link_interrupt_handler(switch_irq, NULL); |
| } |
| #endif /* CONFIG_MV_GTW_LINK_STATUS */ |
| |
| |
| /*********************************************************** |
| * gtw_init_complete -- * |
| * complete all initializations relevant for Gateway. * |
| ***********************************************************/ |
| int __init mv_gtw_init_complete(mv_eth_priv* priv) |
| { |
| int status = 0; |
| |
| status = mv_switch_init(priv->port); |
| if (status != 0) |
| return status; |
| |
| status = mv_eth_start_internals(priv, gtw_config.mtu); |
| if (status != 0) |
| return status; |
| |
| status = mvEthHeaderModeSet(priv->hal_priv, MV_ETH_ENABLE_HEADER_MODE_PRI_2_1); |
| if (status != 0) |
| return status; |
| |
| /* Mask interrupts */ |
| mv_eth_mask_interrupts(priv); |
| |
| #ifdef CONFIG_MV_GTW_IGMP |
| /* Initialize the IGMP snooping handler */ |
| if(mv_gtw_igmp_snoop_init()) { |
| printk("failed to init IGMP snooping handler\n"); |
| } |
| #endif |
| |
| #ifdef CONFIG_MV_GTW_LINK_STATUS |
| if (switch_irq != -1) { |
| if(request_irq(switch_irq, mv_gtw_link_interrupt_handler, |
| (IRQF_DISABLED | IRQF_SAMPLE_RANDOM), "link status", NULL)) |
| { |
| printk(KERN_ERR "failed to assign irq%d\n", switch_irq); |
| } |
| } |
| else { |
| memset( &switch_link_timer, 0, sizeof(struct timer_list) ); |
| init_timer(&switch_link_timer); |
| switch_link_timer.function = mv_gtw_link_timer_function; |
| switch_link_timer.data = -1; |
| switch_link_timer.expires = jiffies + (HZ); /* 1 second */ |
| add_timer(&switch_link_timer); |
| } |
| #endif /* CONFIG_MV_GTW_LINK_STATUS */ |
| |
| return 0; |
| } |
| |
| #define QD_FMT "%10lu %10lu %10lu %10lu %10lu %10lu\n" |
| #define QD_CNT(c,f) c[0].f, c[1].f,c[2].f,c[3].f,c[4].f,c[5].f |
| #define QD_MAX 6 |
| void mv_gtw_switch_stats(int port) |
| { |
| GT_STATS_COUNTER_SET3 counters[QD_MAX]; |
| int p; |
| |
| if(qd_dev == NULL) { |
| printk("Switch is not initialized\n"); |
| return; |
| } |
| memset(counters, 0, sizeof(GT_STATS_COUNTER_SET3) * QD_MAX); |
| |
| for (p=0; p<QD_MAX; p++) |
| gstatsGetPortAllCounters3(qd_dev, p, &counters[p]); |
| |
| printk("PortNum " QD_FMT, (GT_U32)0, (GT_U32)1, (GT_U32)2, (GT_U32)3, (GT_U32)4, (GT_U32)5); |
| printk("-------------------------------------------------------------------------------------\n"); |
| printk("InGoodOctetsLo " QD_FMT, QD_CNT(counters,InGoodOctetsLo)); |
| printk("InGoodOctetsHi " QD_FMT, QD_CNT(counters,InGoodOctetsHi)); |
| printk("InBadOctets " QD_FMT, QD_CNT(counters,InBadOctets)); |
| printk("OutFCSErr " QD_FMT, QD_CNT(counters,OutFCSErr)); |
| printk("Deferred " QD_FMT, QD_CNT(counters,Deferred)); |
| printk("InBroadcasts " QD_FMT, QD_CNT(counters,InBroadcasts)); |
| printk("InMulticasts " QD_FMT, QD_CNT(counters,InMulticasts)); |
| printk("Octets64 " QD_FMT, QD_CNT(counters,Octets64)); |
| printk("Octets127 " QD_FMT, QD_CNT(counters,Octets127)); |
| printk("Octets255 " QD_FMT, QD_CNT(counters,Octets255)); |
| printk("Octets511 " QD_FMT, QD_CNT(counters,Octets511)); |
| printk("Octets1023 " QD_FMT, QD_CNT(counters,Octets1023)); |
| printk("OctetsMax " QD_FMT, QD_CNT(counters,OctetsMax)); |
| printk("OutOctetsLo " QD_FMT, QD_CNT(counters,OutOctetsLo)); |
| printk("OutOctetsHi " QD_FMT, QD_CNT(counters,OutOctetsHi)); |
| printk("OutUnicasts " QD_FMT, QD_CNT(counters,OutOctetsHi)); |
| printk("Excessive " QD_FMT, QD_CNT(counters,Excessive)); |
| printk("OutMulticasts " QD_FMT, QD_CNT(counters,OutMulticasts)); |
| printk("OutBroadcasts " QD_FMT, QD_CNT(counters,OutBroadcasts)); |
| printk("Single " QD_FMT, QD_CNT(counters,OutBroadcasts)); |
| printk("OutPause " QD_FMT, QD_CNT(counters,OutPause)); |
| printk("InPause " QD_FMT, QD_CNT(counters,InPause)); |
| printk("Multiple " QD_FMT, QD_CNT(counters,InPause)); |
| printk("Undersize " QD_FMT, QD_CNT(counters,Undersize)); |
| printk("Fragments " QD_FMT, QD_CNT(counters,Fragments)); |
| printk("Oversize " QD_FMT, QD_CNT(counters,Oversize)); |
| printk("Jabber " QD_FMT, QD_CNT(counters,Jabber)); |
| printk("InMACRcvErr " QD_FMT, QD_CNT(counters,InMACRcvErr)); |
| printk("InFCSErr " QD_FMT, QD_CNT(counters,InFCSErr)); |
| printk("Collisions " QD_FMT, QD_CNT(counters,Collisions)); |
| printk("Late " QD_FMT, QD_CNT(counters,Late)); |
| |
| gstatsFlushAll(qd_dev); |
| } |