blob: 910991e8869836fc3d10f1eac518e6dc9d1ef08c [file] [log] [blame]
/*******************************************************************************
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 = &gtw_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(&gtw_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=&gtw_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=&gtw_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, &gt_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);
}