Skip to content

Commit

Permalink
net: ethernet: mediatek: add ethtool functions to configure RX flows …
Browse files Browse the repository at this point in the history
…of HW LRO

The codes add ethtool functions to set RX flows for HW LRO. Because the
HW LRO hardware can only recognize the destination IP of TCP/IP RX flows,
the ethtool command to add HW LRO flow is as below:
ethtool -N [devname] flow-type tcp4 dst-ip [ip_addr] loc [0~1]

Otherwise, cause the hardware can set total four destination IPs, each
GMAC (GMAC1/GMAC2) can set two IPs separately at most.

Signed-off-by: Nelson Chang <nelson.chang@mediatek.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Nelson Chang authored and David S. Miller committed Sep 19, 2016
1 parent ee40681 commit 7aab747
Showing 1 changed file with 236 additions and 0 deletions.
236 changes: 236 additions & 0 deletions drivers/net/ethernet/mediatek/mtk_eth_soc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,182 @@ static void mtk_hwlro_rx_uninit(struct mtk_eth *eth)
mtk_w32(eth, 0, MTK_PDMA_LRO_CTRL_DW0);
}

static void mtk_hwlro_val_ipaddr(struct mtk_eth *eth, int idx, __be32 ip)
{
u32 reg_val;

reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(idx));

/* invalidate the IP setting */
mtk_w32(eth, (reg_val & ~MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(idx));

mtk_w32(eth, ip, MTK_LRO_DIP_DW0_CFG(idx));

/* validate the IP setting */
mtk_w32(eth, (reg_val | MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(idx));
}

static void mtk_hwlro_inval_ipaddr(struct mtk_eth *eth, int idx)
{
u32 reg_val;

reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(idx));

/* invalidate the IP setting */
mtk_w32(eth, (reg_val & ~MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(idx));

mtk_w32(eth, 0, MTK_LRO_DIP_DW0_CFG(idx));
}

static int mtk_hwlro_get_ip_cnt(struct mtk_mac *mac)
{
int cnt = 0;
int i;

for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) {
if (mac->hwlro_ip[i])
cnt++;
}

return cnt;
}

static int mtk_hwlro_add_ipaddr(struct net_device *dev,
struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
int hwlro_idx;

if ((fsp->flow_type != TCP_V4_FLOW) ||
(!fsp->h_u.tcp_ip4_spec.ip4dst) ||
(fsp->location > 1))
return -EINVAL;

mac->hwlro_ip[fsp->location] = htonl(fsp->h_u.tcp_ip4_spec.ip4dst);
hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + fsp->location;

mac->hwlro_ip_cnt = mtk_hwlro_get_ip_cnt(mac);

mtk_hwlro_val_ipaddr(eth, hwlro_idx, mac->hwlro_ip[fsp->location]);

return 0;
}

static int mtk_hwlro_del_ipaddr(struct net_device *dev,
struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
int hwlro_idx;

if (fsp->location > 1)
return -EINVAL;

mac->hwlro_ip[fsp->location] = 0;
hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + fsp->location;

mac->hwlro_ip_cnt = mtk_hwlro_get_ip_cnt(mac);

mtk_hwlro_inval_ipaddr(eth, hwlro_idx);

return 0;
}

static void mtk_hwlro_netdev_disable(struct net_device *dev)
{
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
int i, hwlro_idx;

for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) {
mac->hwlro_ip[i] = 0;
hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + i;

mtk_hwlro_inval_ipaddr(eth, hwlro_idx);
}

mac->hwlro_ip_cnt = 0;
}

static int mtk_hwlro_get_fdir_entry(struct net_device *dev,
struct ethtool_rxnfc *cmd)
{
struct mtk_mac *mac = netdev_priv(dev);
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;

/* only tcp dst ipv4 is meaningful, others are meaningless */
fsp->flow_type = TCP_V4_FLOW;
fsp->h_u.tcp_ip4_spec.ip4dst = ntohl(mac->hwlro_ip[fsp->location]);
fsp->m_u.tcp_ip4_spec.ip4dst = 0;

fsp->h_u.tcp_ip4_spec.ip4src = 0;
fsp->m_u.tcp_ip4_spec.ip4src = 0xffffffff;
fsp->h_u.tcp_ip4_spec.psrc = 0;
fsp->m_u.tcp_ip4_spec.psrc = 0xffff;
fsp->h_u.tcp_ip4_spec.pdst = 0;
fsp->m_u.tcp_ip4_spec.pdst = 0xffff;
fsp->h_u.tcp_ip4_spec.tos = 0;
fsp->m_u.tcp_ip4_spec.tos = 0xff;

return 0;
}

static int mtk_hwlro_get_fdir_all(struct net_device *dev,
struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
struct mtk_mac *mac = netdev_priv(dev);
int cnt = 0;
int i;

for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) {
if (mac->hwlro_ip[i]) {
rule_locs[cnt] = i;
cnt++;
}
}

cmd->rule_cnt = cnt;

return 0;
}

static netdev_features_t mtk_fix_features(struct net_device *dev,
netdev_features_t features)
{
if (!(features & NETIF_F_LRO)) {
struct mtk_mac *mac = netdev_priv(dev);
int ip_cnt = mtk_hwlro_get_ip_cnt(mac);

if (ip_cnt) {
netdev_info(dev, "RX flow is programmed, LRO should keep on\n");

features |= NETIF_F_LRO;
}
}

return features;
}

static int mtk_set_features(struct net_device *dev, netdev_features_t features)
{
int err = 0;

if (!((dev->features ^ features) & NETIF_F_LRO))
return 0;

if (!(features & NETIF_F_LRO))
mtk_hwlro_netdev_disable(dev);

return err;
}

/* wait for DMA to finish whatever it is doing before we start using it again */
static int mtk_dma_busy_wait(struct mtk_eth *eth)
{
Expand Down Expand Up @@ -1971,6 +2147,62 @@ static void mtk_get_ethtool_stats(struct net_device *dev,
} while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
}

static int mtk_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
int ret = -EOPNOTSUPP;

switch (cmd->cmd) {
case ETHTOOL_GRXRINGS:
if (dev->features & NETIF_F_LRO) {
cmd->data = MTK_MAX_RX_RING_NUM;
ret = 0;
}
break;
case ETHTOOL_GRXCLSRLCNT:
if (dev->features & NETIF_F_LRO) {
struct mtk_mac *mac = netdev_priv(dev);

cmd->rule_cnt = mac->hwlro_ip_cnt;
ret = 0;
}
break;
case ETHTOOL_GRXCLSRULE:
if (dev->features & NETIF_F_LRO)
ret = mtk_hwlro_get_fdir_entry(dev, cmd);
break;
case ETHTOOL_GRXCLSRLALL:
if (dev->features & NETIF_F_LRO)
ret = mtk_hwlro_get_fdir_all(dev, cmd,
rule_locs);
break;
default:
break;
}

return ret;
}

static int mtk_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
{
int ret = -EOPNOTSUPP;

switch (cmd->cmd) {
case ETHTOOL_SRXCLSRLINS:
if (dev->features & NETIF_F_LRO)
ret = mtk_hwlro_add_ipaddr(dev, cmd);
break;
case ETHTOOL_SRXCLSRLDEL:
if (dev->features & NETIF_F_LRO)
ret = mtk_hwlro_del_ipaddr(dev, cmd);
break;
default:
break;
}

return ret;
}

static const struct ethtool_ops mtk_ethtool_ops = {
.get_settings = mtk_get_settings,
.set_settings = mtk_set_settings,
Expand All @@ -1982,6 +2214,8 @@ static const struct ethtool_ops mtk_ethtool_ops = {
.get_strings = mtk_get_strings,
.get_sset_count = mtk_get_sset_count,
.get_ethtool_stats = mtk_get_ethtool_stats,
.get_rxnfc = mtk_get_rxnfc,
.set_rxnfc = mtk_set_rxnfc,
};

static const struct net_device_ops mtk_netdev_ops = {
Expand All @@ -1996,6 +2230,8 @@ static const struct net_device_ops mtk_netdev_ops = {
.ndo_change_mtu = eth_change_mtu,
.ndo_tx_timeout = mtk_tx_timeout,
.ndo_get_stats64 = mtk_get_stats64,
.ndo_fix_features = mtk_fix_features,
.ndo_set_features = mtk_set_features,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = mtk_poll_controller,
#endif
Expand Down

0 comments on commit 7aab747

Please sign in to comment.