blob: ebc8e72f1f1132bd0d10ccb035e20f8847117dbc [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 "mvOs.h"
#include "dbg-trace.h"
#include "mvSysHwConfig.h"
#include "boardEnv/mvBoardEnvLib.h"
#ifdef MV_INCLUDE_ETH_COMPLEX
#include "ctrlEnv/mvCtrlEthCompLib.h"
#endif /* MV_INCLUDE_ETH_COMPLEX */
#include "gbe/mvNeta.h"
#include "mv_switch.h"
#include "mv_netdev.h"
extern int mv_net_devs_num;
/* Example: "mv_net_config=4,(00:99:88:88:99:77,0)(00:55:44:55:66:77,1:2:3:4)(00:11:22:33:44:55,),mtu=1500" */
static char *net_config_str[CONFIG_MV_ETH_PORTS_NUM] = {NULL};
struct mv_eth_switch_config switch_net_config[CONFIG_MV_ETH_PORTS_NUM];
static int mv_eth_switch_started = 0;
unsigned int switch_enabled_ports = 0;
/* Required to get the configuration string from the Kernel Command Line */
int mv_eth0_cmdline_config(char *s);
__setup("mv_net_config=", mv_eth0_cmdline_config);
int mv_eth0_cmdline_config(char *s)
{
net_config_str[MV_ETH_PORT_0] = s;
return 1;
}
int mv_eth1_cmdline_config(char *s);
__setup("mv_net_config1=", mv_eth1_cmdline_config);
int mv_eth1_cmdline_config(char *s)
{
net_config_str[MV_ETH_PORT_1] = s;
return 1;
}
/* Local function prototypes */
static int mv_eth_check_open_bracket(char **p_net_config)
{
if (**p_net_config == '(') {
(*p_net_config)++;
return 0;
}
printk(KERN_ERR "Syntax error: could not find opening bracket\n");
return -EINVAL;
}
static int mv_eth_check_closing_bracket(char **p_net_config)
{
if (**p_net_config == ')') {
(*p_net_config)++;
return 0;
}
printk(KERN_ERR "Syntax error: could not find closing bracket\n");
return -EINVAL;
}
static int mv_eth_check_comma(char **p_net_config)
{
if (**p_net_config == ',') {
(*p_net_config)++;
return 0;
}
printk(KERN_ERR "Syntax error: could not find comma\n");
return -EINVAL;
}
static int mv_eth_netconfig_mac_addr_get(char **p_net_config, int idx, int port)
{
int num;
char *config_str = *p_net_config;
MV_U32 mac[MV_MAC_ADDR_SIZE];
/* the MAC address should look like: 00:99:88:88:99:77 */
/* that is, 6 two-digit numbers, separated by : */
num = sscanf(config_str, "%2x:%2x:%2x:%2x:%2x:%2x",
&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
if (num == MV_MAC_ADDR_SIZE) {
while (--num >= 0)
switch_net_config[port].mac_addr[idx][num] = (mac[num] & 0xFF);
(*p_net_config) = config_str + 17;
return 0;
}
printk(KERN_ERR "Syntax error while parsing MAC address from command line\n");
return -EINVAL;
}
static int mv_eth_netconfig_ports_get(char **p_net_config, int idx, int port)
{
char ch;
char *config_str = *p_net_config;
int sw_port, mask = 0, status = -EINVAL;
/* the switch port list should look like this: */
/* example 0: ) - no ports */
/* example 1: 0) - single port 0 */
/* example 2: 1:2:3:4) - multiple ports */
while (1) {
ch = *config_str++;
if (ch == ')') {
/* Finished */
status = 0;
break;
}
sw_port = mvCharToDigit(ch);
if (sw_port < 0)
break;
/* TBD - Check sw_port validity */
mask |= (1 << sw_port);
if (*config_str == ':')
config_str++;
}
*p_net_config = config_str;
if (status == 0) {
switch_net_config[port].board_port_map[idx] = mask;
return 0;
}
printk(KERN_ERR "Syntax error while parsing switch port mask from command line\n");
return -EINVAL;
}
/* the mtu value is constructed as follows: */
/* mtu=value */
static int mv_eth_netconfig_mtu_get(char **p_net_config, int port)
{
unsigned int mtu;
if (strncmp(*p_net_config, "mtu=", 4) == 0) {
*p_net_config += 4;
mtu = strtol(*p_net_config, p_net_config, 10);
if (mtu > 0) {
switch_net_config[port].mtu = mtu;
printk(KERN_ERR " o MTU set to %d.\n", mtu);
return 0;
}
printk(KERN_ERR "Syntax error while parsing mtu value from command line\n");
return -EINVAL;
}
switch_net_config[port].mtu = 1500;
printk(KERN_ERR " o Using default MTU %d\n", switch_net_config[port].mtu);
return 0;
}
static int mv_eth_netconfig_max_get(char **p_net_config, int port)
{
char num = **p_net_config;
int netdev_num;
netdev_num = mvCharToDigit(num);
if (netdev_num >= 0) {
switch_net_config[port].netdev_max = netdev_num;
(*p_net_config) += 1;
return 0;
}
printk(KERN_ERR "Syntax error while parsing number of netdevs from command line\n");
return -EINVAL;
}
int mv_eth_switch_config_get(int use_existing_config, int port)
{
char *p_net_config;
int i = 0;
if ((port != MV_ETH_PORT_0) && (port != MV_ETH_PORT_1)) {
printk("%s: invalid port number %d\n", __func__, port);
return -EINVAL;
}
if (!use_existing_config) {
memset(&(switch_net_config[port]), 0, sizeof((switch_net_config[port])));
if (net_config_str[port] != NULL) {
printk(KERN_ERR " o Using UBoot netconfig string for port %d\n", port);
}
else {
printk(KERN_ERR " o Using default netconfig string from Kconfig for port %d\n", port);
if (port == MV_ETH_PORT_0)
net_config_str[port] = CONFIG_MV_ETH_SWITCH_NETCONFIG_0;
else if (port == MV_ETH_PORT_1)
net_config_str[port] = CONFIG_MV_ETH_SWITCH_NETCONFIG_1;
}
printk(KERN_ERR " net_config_str[%d]: %s\n", port, net_config_str[port]);
p_net_config = net_config_str[port];
if (mv_eth_netconfig_max_get(&p_net_config, port))
return -EINVAL;
/* check restriction: at least one of the configuration strings must be 0 */
if ((net_config_str[MV_ETH_PORT_0] != NULL) &&
(net_config_str[MV_ETH_PORT_1] != NULL) &&
(switch_net_config[MV_ETH_PORT_0].netdev_max != 0) &&
(switch_net_config[MV_ETH_PORT_1].netdev_max != 0)) {
printk("%s: cannot have both GbE ports using the Gateway driver, change mv_net_config\n", __func__);
return -EINVAL;
}
if (switch_net_config[port].netdev_max == 0)
return 1;
if (switch_net_config[port].netdev_max > CONFIG_MV_ETH_SWITCH_NETDEV_NUM) {
printk(KERN_ERR "Too large number of netdevs (%d) in command line: cut to %d\n",
switch_net_config[port].netdev_max, CONFIG_MV_ETH_SWITCH_NETDEV_NUM);
switch_net_config[port].netdev_max = CONFIG_MV_ETH_SWITCH_NETDEV_NUM;
}
if (mv_eth_check_comma(&p_net_config))
return -EINVAL;
for (i = 0; (i < CONFIG_MV_ETH_SWITCH_NETDEV_NUM) && (*p_net_config != '\0'); i++) {
if (mv_eth_check_open_bracket(&p_net_config))
return -EINVAL;
if (mv_eth_netconfig_mac_addr_get(&p_net_config, i, port))
return -EINVAL;
if (mv_eth_check_comma(&p_net_config))
return -EINVAL;
if (mv_eth_netconfig_ports_get(&p_net_config, i, port))
return -EINVAL;
switch_net_config[port].netdev_cfg++;
/* If we have a comma after the closing bracket, then interface */
/* definition is done. */
if (*p_net_config == ',') {
p_net_config++;
break;
}
}
/* there is a chance the previous loop did not end because a comma was found but because */
/* the maximum number of interfaces was reached, so check for the comma now. */
if (i == CONFIG_MV_ETH_SWITCH_NETDEV_NUM)
if (mv_eth_check_comma(&p_net_config))
return -EINVAL;
if (*p_net_config != '\0') {
if (mv_eth_netconfig_mtu_get(&p_net_config, port))
return -EINVAL;
} else {
switch_net_config[port].mtu = 1500;
printk(KERN_ERR " o Using default MTU %d\n", switch_net_config[port].mtu);
}
/* at this point, we have parsed up to CONFIG_MV_ETH_SWITCH_NETDEV_NUM, and the mtu value */
/* if the net_config string is not finished yet, then its format is invalid */
if (*p_net_config != '\0') {
printk(KERN_ERR "Switch netconfig string is too long: %s\n", p_net_config);
return -EINVAL;
}
} else {
/* leave most of the configuration as-is, but update MAC addresses */
/* MTU is saved in mv_eth_switch_change_mtu */
/* Note: at this point, since this is a re-init, mv_eth_switch_netdev_first */
/* and mv_eth_switch_netdev_last, as well as mv_net_devs[], are valid. */
for (i = mv_eth_switch_netdev_first; i <= mv_eth_switch_netdev_last; i++) {
if (mv_net_devs[i] != NULL)
memcpy(switch_net_config[port].mac_addr[i - mv_eth_switch_netdev_first],
mv_net_devs[i]->dev_addr, MV_MAC_ADDR_SIZE);
}
if (switch_net_config[port].netdev_max == 0)
return 1;
}
return 0;
}
int mv_eth_switch_set_mac_addr(struct net_device *dev, void *mac)
{
struct eth_netdev *dev_priv = MV_DEV_PRIV(dev);
struct sockaddr *addr = (struct sockaddr *)mac;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
/* remove old mac addr from VLAN DB */
mv_switch_mac_addr_set(dev->dev_addr, MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id), (1 << dev_priv->cpu_port), 0);
memcpy(dev->dev_addr, addr->sa_data, MV_MAC_ADDR_SIZE);
/* add new mac addr to VLAN DB */
mv_switch_mac_addr_set(dev->dev_addr, MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id), (1 << dev_priv->cpu_port), 1);
printk(KERN_ERR "mv_eth_switch: %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_eth_switch_set_multicast_list(struct net_device *dev)
{
struct eth_netdev *dev_priv = MV_DEV_PRIV(dev);
if (dev->flags & IFF_PROMISC) {
/* promiscuous mode - connect the CPU port to the VLAN (port based + 802.1q) */
/* printk(KERN_ERR "mv_eth_switch: setting promiscuous mode\n"); */
if (mv_switch_promisc_set(dev_priv->vlan_grp_id, dev_priv->port_map, dev_priv->cpu_port, 1))
printk(KERN_ERR "mv_switch_promisc_set to 1 failed\n");
} else {
/* not in promiscuous mode - disconnect the CPU port to the VLAN (port based + 802.1q) */
if (mv_switch_promisc_set(dev_priv->vlan_grp_id, dev_priv->port_map, dev_priv->cpu_port, 0))
printk(KERN_ERR "mv_switch_promisc_set to 0 failed\n");
if (dev->flags & IFF_ALLMULTI) {
/* allmulticast - not supported. There is no way to tell the Switch to accept only */
/* the multicast addresses but not Unicast addresses, so the alternatives are: */
/* 1) Don't support multicast and do nothing */
/* 2) Support multicast with same implementation as promiscuous */
/* 3) Don't rely on Switch for MAC filtering, but use PnC */
/* Currently option 1 is selected */
printk(KERN_ERR "mv_eth_switch: setting all-multicast mode is not supported\n");
}
/* Add or delete specific multicast addresses: */
/* Linux provides a list of the current multicast addresses for the device. */
/* First, we delete all the multicast addresses in the ATU. */
/* Then we add the specific multicast addresses Linux provides. */
if (mv_switch_all_multicasts_del(MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id)))
printk(KERN_ERR "mv_eth_switch: mv_switch_all_multicasts_del failed\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
if (!netdev_mc_empty(dev)) {
struct netdev_hw_addr *ha;
netdev_for_each_mc_addr(ha, dev) {
mv_switch_mac_addr_set(ha->addr,
MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id),
(dev_priv->port_map | (1 << dev_priv->cpu_port)), 1);
}
}
#else
{
int i;
struct dev_mc_list *curr_addr = dev->mc_list;
/* accept specific multicasts */
for (i = 0; i < dev->mc_count; i++, curr_addr = curr_addr->next) {
if (!curr_addr)
break;
/*
printk(KERN_ERR "Setting MC = %02X:%02X:%02X:%02X:%02X:%02X\n",
curr_addr->dmi_addr[0], curr_addr->dmi_addr[1], curr_addr->dmi_addr[2],
curr_addr->dmi_addr[3], curr_addr->dmi_addr[4], curr_addr->dmi_addr[5]);
*/
mv_switch_mac_addr_set(curr_addr->dmi_addr,
MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id),
(dev_priv->port_map | (1 << dev_priv->cpu_port)), 1);
}
}
#endif /* KERNEL_VERSION >= 2.6.34 */
}
}
int mv_eth_switch_change_mtu(struct net_device *dev, int mtu)
{
int i;
struct eth_port *priv = MV_ETH_PRIV(dev);
/* All gateway interfaces must be down before changing MTU */
for (i = mv_eth_switch_netdev_first; i <= mv_eth_switch_netdev_last; i++) {
if ((mv_net_devs[i] != NULL) && (netif_running(mv_net_devs[i]))) {
printk(KERN_ERR "All gateway interfaces must be down before changing MTU. %s is not down\n", mv_net_devs[i]->name);
return -EPERM;
}
}
if (dev->mtu != mtu) {
int old_mtu = dev->mtu;
mtu = mv_eth_check_mtu_valid(dev, mtu);
if (mtu < 0)
return -EPERM;
if (mv_eth_change_mtu_internals(dev, mtu))
return -EPERM;
printk(KERN_NOTICE "%s: change mtu %d (pkt-size %d) to %d (pkt-size %d)\n",
dev->name, old_mtu, RX_PKT_SIZE(old_mtu),
dev->mtu, RX_PKT_SIZE(dev->mtu));
}
if (switch_net_config[priv->port].mtu != mtu) {
mv_switch_jumbo_mode_set(RX_PKT_SIZE(mtu));
switch_net_config[priv->port].mtu = mtu;
}
return 0;
}
int mv_eth_switch_start(struct net_device *dev)
{
struct eth_port *priv = MV_ETH_PRIV(dev);
struct eth_netdev *dev_priv = MV_DEV_PRIV(dev);
unsigned char broadcast[MV_MAC_ADDR_SIZE] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
int i;
int group;
/* Check that MTU value is identical for all gateway interfaces */
for (i = mv_eth_switch_netdev_first; i <= mv_eth_switch_netdev_last; i++) {
if ((mv_net_devs[i] != NULL) && (mv_net_devs[i]->mtu != dev->mtu)) {
printk(KERN_ERR "All gateway devices must have same MTU\n");
return -EPERM;
}
}
/* in default link is down */
netif_carrier_off(dev);
/* Stop the TX queue - it will be enabled upon PHY status change after link-up interrupt/timer */
netif_stop_queue(dev);
/* start upper layer accordingly with ports_map */
#ifdef CONFIG_MV_ETH_SWITCH_LINK
dev_priv->link_map = 0;
mv_switch_link_update_event(dev_priv->port_map, 1);
#else
dev_priv->link_map = dev_priv->port_map;
#endif /* CONFIG_MV_ETH_SWITCH_LINK */
if (mv_eth_switch_started == 0) {
/* fill rx buffers, start rx/tx activity, set coalescing */
if (mv_eth_start_internals(priv, dev->mtu) != 0) {
printk(KERN_ERR "%s: start internals failed\n", dev->name);
goto error;
}
/* enable polling on the port, must be used after netif_poll_disable */
for (group = 0; group < CONFIG_MV_ETH_NAPI_GROUPS; group++)
napi_enable(priv->napiGroup[group]);
/* connect to port interrupt line */
if (request_irq(dev->irq, mv_eth_isr, IRQF_DISABLED|IRQF_SAMPLE_RANDOM, "mv_eth", priv)) {
printk(KERN_ERR "cannot request irq %d for %s port %d\n",
dev->irq, dev->name, priv->port);
for (group = 0; group < CONFIG_MV_ETH_NAPI_GROUPS; group++)
napi_disable(priv->napiGroup[group]);
goto error;
}
/* unmask interrupts */
mv_eth_interrupts_unmask(priv);
}
mv_eth_switch_started++;
/* 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_switch_mac_addr_set(dev->dev_addr, MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id), (1 << dev_priv->cpu_port), 1);
/* We also need to allow L2 broadcasts comming up for this interface */
mv_switch_mac_addr_set(broadcast, MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id),
(dev_priv->port_map | (1 << dev_priv->cpu_port)), 1);
printk(KERN_ERR "%s: started (on switch)\n", dev->name);
return 0;
error:
printk(KERN_ERR "%s: start failed\n", dev->name);
return -1;
}
int mv_eth_switch_stop(struct net_device *dev)
{
struct eth_port *priv = MV_ETH_PRIV(dev);
struct eth_netdev *dev_priv = MV_DEV_PRIV(dev);
int group;
/* stop upper layer */
netif_carrier_off(dev);
netif_stop_queue(dev);
/* stop switch from forwarding packets from this VLAN toward CPU port */
mv_switch_atu_db_flush(MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id));
/* It is possible that the interface is in promiscuous mode */
/* If so, the CPU port is connected with port based VLAN to the other ports, and */
/* we must disconnect it now to stop the Switch from forwarding packets to the CPU */
/* when the interface is down. */
/* mv_eth_switch_set_multicast_list will be called anyway by Linux when we do ifconfig up */
/* and will re-set the promiscuous feature if needed */
if (dev->flags & IFF_PROMISC) {
if (mv_switch_promisc_set(dev_priv->vlan_grp_id, dev_priv->port_map, dev_priv->cpu_port, 0))
printk(KERN_ERR "mv_switch_promisc_set to 0 failed\n");
}
mv_eth_switch_started--;
if (mv_eth_switch_started == 0) {
/* first make sure that the port finished its Rx polling - see tg3 */
/* otherwise it may cause issue in SMP, one CPU is here and the other is doing the polling */
/* and both of it are messing with the descriptors rings!! */
for (group = 0; group < CONFIG_MV_ETH_NAPI_GROUPS; group++)
napi_disable(priv->napiGroup[group]);
/* stop tx/rx activity, mask all interrupts, relese skb in rings,*/
mv_eth_stop_internals(priv);
del_timer(&priv->tx_done_timer);
clear_bit(MV_ETH_F_TX_DONE_TIMER_BIT, &(priv->flags));
del_timer(&priv->cleanup_timer);
clear_bit(MV_ETH_F_CLEANUP_TIMER_BIT, &(priv->flags));
if (dev->irq != 0)
free_irq(dev->irq, priv);
}
printk(KERN_NOTICE "%s: stopped\n", dev->name);
return 0;
}
#ifdef CONFIG_MV_ETH_SWITCH_LINK
void mv_eth_switch_interrupt_unmask(int qsgmii_module, int gephy_on_port)
{
#ifdef MV_INCLUDE_ETH_COMPLEX
MV_U32 reg;
reg = MV_REG_READ(MV_ETHCOMP_INT_MAIN_MASK_REG);
if (qsgmii_module) {
reg |= (MV_ETHCOMP_PCS0_LINK_INT_MASK |
MV_ETHCOMP_PCS1_LINK_INT_MASK |
MV_ETHCOMP_PCS2_LINK_INT_MASK |
MV_ETHCOMP_PCS3_LINK_INT_MASK);
}
if (gephy_on_port >= 0)
reg |= MV_ETHCOMP_GEPHY_INT_MASK;
reg |= MV_ETHCOMP_SWITCH_INT_MASK;
MV_REG_WRITE(MV_ETHCOMP_INT_MAIN_MASK_REG, reg);
#endif /* MV_INCLUDE_ETH_COMPLEX */
}
void mv_eth_switch_interrupt_clear(int qsgmii_module, int gephy_on_port)
{
#ifdef MV_INCLUDE_ETH_COMPLEX
MV_U32 reg;
reg = MV_REG_READ(MV_ETHCOMP_INT_MAIN_CAUSE_REG);
if (qsgmii_module) {
reg &= ~(MV_ETHCOMP_PCS0_LINK_INT_MASK |
MV_ETHCOMP_PCS1_LINK_INT_MASK |
MV_ETHCOMP_PCS2_LINK_INT_MASK |
MV_ETHCOMP_PCS3_LINK_INT_MASK);
}
if (gephy_on_port >= 0)
reg &= ~MV_ETHCOMP_GEPHY_INT_MASK;
reg &= ~MV_ETHCOMP_SWITCH_INT_MASK;
MV_REG_WRITE(MV_ETHCOMP_INT_MAIN_CAUSE_REG, reg);
#endif /* MV_INCLUDE_ETH_COMPLEX */
}
void mv_eth_switch_update_link(unsigned int p, unsigned int link_up)
{
struct eth_netdev *dev_priv = NULL;
struct eth_port *priv = NULL;
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;
priv = MV_ETH_PRIV(mv_net_devs[i]);
if (priv == NULL)
break;
if (!(priv->flags & (MV_ETH_F_SWITCH | MV_ETH_F_EXT_SWITCH)))
continue;
dev_priv = MV_DEV_PRIV(mv_net_devs[i]);
if (dev_priv == NULL)
break;
if ((dev_priv->port_map & (1 << p)) == 0)
continue;
prev_ports_link = dev_priv->link_map;
if (link_up)
dev_priv->link_map |= (1 << p);
else
dev_priv->link_map &= ~(1 << p);
if ((prev_ports_link != 0) && (dev_priv->link_map == 0) && netif_running(mv_net_devs[i])) {
/* link down */
netif_carrier_off(mv_net_devs[i]);
netif_stop_queue(mv_net_devs[i]);
printk(KERN_ERR "%s: link down\n", mv_net_devs[i]->name);
} else if ((prev_ports_link == 0) && (dev_priv->link_map != 0) && netif_running(mv_net_devs[i])) {
/* link up */
if (mv_eth_ctrl_is_tx_enabled(priv) == 1) {
netif_carrier_on(mv_net_devs[i]);
netif_wake_queue(mv_net_devs[i]);
printk(KERN_ERR "%s: link up\n", mv_net_devs[i]->name);
}
}
}
}
#endif /* CONFIG_MV_ETH_SWITCH_LINK */
int mv_eth_switch_port_add(struct net_device *dev, int port)
{
struct eth_netdev *dev_priv = MV_DEV_PRIV(dev);
struct eth_port *priv = MV_ETH_PRIV(dev);
int i, switch_port, err = 0;
if (dev_priv == NULL) {
printk(KERN_ERR "%s is not connected to the switch\n", dev->name);
return 1;
}
if (netif_running(dev)) {
printk(KERN_ERR "%s must be down to change switch ports map\n", dev->name);
return 1;
}
switch_port = mvBoardSwitchPortGet(0, port);
if (switch_port < 0) {
printk(KERN_ERR "Switch port %d can't be added\n", port);
return 1;
}
if (MV_BIT_CHECK(switch_enabled_ports, switch_port)) {
printk(KERN_ERR "Switch port %d is already enabled\n", port);
return 0;
}
/* Update data base */
dev_priv->port_map |= (1 << switch_port);
for (i = mv_eth_switch_netdev_first; i <= mv_eth_switch_netdev_last; i++) {
if ((mv_net_devs[i] != NULL) && (mv_net_devs[i] == dev))
switch_net_config[priv->port].board_port_map[i - mv_eth_switch_netdev_first] |= (1 << switch_port);
}
switch_enabled_ports |= (1 << switch_port);
dev_priv->tx_vlan_mh = cpu_to_be16((MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id) << 12) | dev_priv->port_map);
err = mv_switch_port_add(switch_port, dev_priv->vlan_grp_id, dev_priv->port_map);
if (!err)
printk(KERN_ERR "%s: Switch port #%d mapped\n", dev->name, port);
return err;
}
int mv_eth_switch_port_del(struct net_device *dev, int port)
{
struct eth_netdev *dev_priv = MV_DEV_PRIV(dev);
struct eth_port *priv = MV_ETH_PRIV(dev);
int i, switch_port, err = 0;
if (dev_priv == NULL) {
printk(KERN_ERR "%s is not connected to the switch\n", dev->name);
return 1;
}
if (netif_running(dev)) {
printk(KERN_ERR "%s must be down to change switch ports map\n", dev->name);
return 1;
}
switch_port = mvBoardSwitchPortGet(0, port);
if (switch_port < 0) {
printk(KERN_ERR "Switch port %d can't be added\n", port);
return 1;
}
if (!MV_BIT_CHECK(switch_enabled_ports, switch_port)) {
printk(KERN_ERR "Switch port %d is already disabled\n", port);
return 0;
}
if (!MV_BIT_CHECK(dev_priv->port_map, switch_port)) {
printk(KERN_ERR "Switch port %d is not mapped on %s\n", port, dev->name);
return 0;
}
/* Update data base */
dev_priv->port_map &= ~(1 << switch_port);
for (i = mv_eth_switch_netdev_first; i <= mv_eth_switch_netdev_last; i++) {
if ((mv_net_devs[i] != NULL) && (mv_net_devs[i] == dev))
switch_net_config[priv->port].board_port_map[i - mv_eth_switch_netdev_first] &= ~(1 << switch_port);
}
dev_priv->link_map &= ~(1 << switch_port);
switch_enabled_ports &= ~(1 << switch_port);
dev_priv->tx_vlan_mh = cpu_to_be16((MV_SWITCH_VLAN_TO_GROUP(dev_priv->vlan_grp_id) << 12) | dev_priv->port_map);
err = mv_switch_port_del(switch_port, dev_priv->vlan_grp_id, dev_priv->port_map);
if (!err)
printk(KERN_ERR "%s: Switch port #%d unmapped\n", dev->name, port);
return err;
}
void mv_eth_switch_status_print(int port)
{
int i;
struct eth_port *pp = mv_eth_port_by_id(port);
struct net_device *dev;
if (pp->flags & MV_ETH_F_SWITCH) {
printk(KERN_ERR "ethPort=%d: mv_eth_switch status - pp=%p, flags=0x%lx\n", port, pp, pp->flags);
printk(KERN_ERR "mtu=%d, netdev_max=%d, netdev_cfg=%d, first=%d, last=%d\n",
switch_net_config[port].mtu, switch_net_config[port].netdev_max, switch_net_config[port].netdev_cfg,
mv_eth_switch_netdev_first, mv_eth_switch_netdev_last);
for (i = 0; i < switch_net_config[port].netdev_cfg; i++) {
printk(KERN_ERR "MAC="MV_MACQUAD_FMT", board_port_map=0x%x\n",
MV_MACQUAD(switch_net_config[port].mac_addr[i]), switch_net_config[port].board_port_map[i]);
}
for (i = mv_eth_switch_netdev_first; i <= mv_eth_switch_netdev_last; i++) {
dev = mv_eth_netdev_by_id(i);
if (dev)
mv_eth_netdev_print(dev);
}
} else
printk(KERN_ERR "ethPort=%d: switch is not connected - pp=%p, flags=0x%lx\n", port, pp, pp->flags);
}