diff options
author | Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> | 2012-02-15 17:55:06 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-02-16 17:08:09 -0500 |
commit | 71cc7c37af71b497698f7f8a68e46a458071fcef (patch) | |
tree | 5ac6cf763fd29fe93b02b2eed8404276064d1c87 /drivers/net/ethernet | |
parent | 6743fe6df43b4dc5950f605edfeee086d0a80f06 (diff) |
net: sh_eth: add support for VLAN tag filtering
Some controllers have TSU. It can register one VLAN tag, and it can
filter other VLAN tag by hardware.
If vlan_rx_add_vid() is called twice or more, the driver will disable
the filtering.
Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/renesas/sh_eth.c | 59 | ||||
-rw-r--r-- | drivers/net/ethernet/renesas/sh_eth.h | 5 |
2 files changed, 64 insertions, 0 deletions
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 99d8ce8379c..8615961c128 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1888,6 +1888,62 @@ static void sh_eth_set_multicast_list(struct net_device *ndev) spin_unlock_irqrestore(&mdp->lock, flags); } + +static int sh_eth_get_vtag_index(struct sh_eth_private *mdp) +{ + if (!mdp->port) + return TSU_VTAG0; + else + return TSU_VTAG1; +} + +static int sh_eth_vlan_rx_add_vid(struct net_device *ndev, u16 vid) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + int vtag_reg_index = sh_eth_get_vtag_index(mdp); + + if (unlikely(!mdp->cd->tsu)) + return -EPERM; + + /* No filtering if vid = 0 */ + if (!vid) + return 0; + + mdp->vlan_num_ids++; + + /* + * The controller has one VLAN tag HW filter. So, if the filter is + * already enabled, the driver disables it and the filte + */ + if (mdp->vlan_num_ids > 1) { + /* disable VLAN filter */ + sh_eth_tsu_write(mdp, 0, vtag_reg_index); + return 0; + } + + sh_eth_tsu_write(mdp, TSU_VTAG_ENABLE | (vid & TSU_VTAG_VID_MASK), + vtag_reg_index); + + return 0; +} + +static int sh_eth_vlan_rx_kill_vid(struct net_device *ndev, u16 vid) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + int vtag_reg_index = sh_eth_get_vtag_index(mdp); + + if (unlikely(!mdp->cd->tsu)) + return -EPERM; + + /* No filtering if vid = 0 */ + if (!vid) + return 0; + + mdp->vlan_num_ids--; + sh_eth_tsu_write(mdp, 0, vtag_reg_index); + + return 0; +} #endif /* SH_ETH_HAS_TSU */ /* SuperH's TSU register init function */ @@ -2037,6 +2093,8 @@ static const struct net_device_ops sh_eth_netdev_ops = { .ndo_get_stats = sh_eth_get_stats, #if defined(SH_ETH_HAS_TSU) .ndo_set_rx_mode = sh_eth_set_multicast_list, + .ndo_vlan_rx_add_vid = sh_eth_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = sh_eth_vlan_rx_kill_vid, #endif .ndo_tx_timeout = sh_eth_tx_timeout, .ndo_do_ioctl = sh_eth_do_ioctl, @@ -2141,6 +2199,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) mdp->tsu_addr = ioremap(rtsu->start, resource_size(rtsu)); mdp->port = devno % 2; + ndev->features = NETIF_F_HW_VLAN_FILTER; } /* initialize first or needed device */ diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 86b392e22c0..57dc2626111 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -679,6 +679,10 @@ enum TSU_FWSLC_BIT { TSU_FWSLC_CAMSEL11 = 0x0002, TSU_FWSLC_CAMSEL10 = 0x0001, }; +/* TSU_VTAGn */ +#define TSU_VTAG_ENABLE 0x80000000 +#define TSU_VTAG_VID_MASK 0x00000fff + /* * The sh ether Tx buffer descriptors. * This structure should be 20 bytes. @@ -781,6 +785,7 @@ struct sh_eth_private { char post_fw; /* POST forward */ struct net_device_stats tsu_stats; /* TSU forward status */ int port; /* for TSU */ + int vlan_num_ids; /* for VLAN tag filter */ unsigned no_ether_link:1; unsigned ether_link_active_low:1; |