162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/net/ethernet/freescale/gianfar_ethtool.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Gianfar Ethernet Driver 662306a36Sopenharmony_ci * Ethtool support for Gianfar Enet 762306a36Sopenharmony_ci * Based on e1000 ethtool support 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Author: Andy Fleming 1062306a36Sopenharmony_ci * Maintainer: Kumar Gala 1162306a36Sopenharmony_ci * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com> 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Copyright 2003-2006, 2008-2009, 2011 Freescale Semiconductor, Inc. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/netdevice.h> 2462306a36Sopenharmony_ci#include <linux/etherdevice.h> 2562306a36Sopenharmony_ci#include <linux/net_tstamp.h> 2662306a36Sopenharmony_ci#include <linux/skbuff.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/mm.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <asm/io.h> 3162306a36Sopenharmony_ci#include <asm/irq.h> 3262306a36Sopenharmony_ci#include <linux/uaccess.h> 3362306a36Sopenharmony_ci#include <linux/module.h> 3462306a36Sopenharmony_ci#include <linux/crc32.h> 3562306a36Sopenharmony_ci#include <asm/types.h> 3662306a36Sopenharmony_ci#include <linux/ethtool.h> 3762306a36Sopenharmony_ci#include <linux/mii.h> 3862306a36Sopenharmony_ci#include <linux/phy.h> 3962306a36Sopenharmony_ci#include <linux/sort.h> 4062306a36Sopenharmony_ci#include <linux/if_vlan.h> 4162306a36Sopenharmony_ci#include <linux/of.h> 4262306a36Sopenharmony_ci#include <linux/of_platform.h> 4362306a36Sopenharmony_ci#include <linux/platform_device.h> 4462306a36Sopenharmony_ci#include <linux/fsl/ptp_qoriq.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include "gianfar.h" 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define GFAR_MAX_COAL_USECS 0xffff 4962306a36Sopenharmony_ci#define GFAR_MAX_COAL_FRAMES 0xff 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic const char stat_gstrings[][ETH_GSTRING_LEN] = { 5262306a36Sopenharmony_ci /* extra stats */ 5362306a36Sopenharmony_ci "rx-allocation-errors", 5462306a36Sopenharmony_ci "rx-large-frame-errors", 5562306a36Sopenharmony_ci "rx-short-frame-errors", 5662306a36Sopenharmony_ci "rx-non-octet-errors", 5762306a36Sopenharmony_ci "rx-crc-errors", 5862306a36Sopenharmony_ci "rx-overrun-errors", 5962306a36Sopenharmony_ci "rx-busy-errors", 6062306a36Sopenharmony_ci "rx-babbling-errors", 6162306a36Sopenharmony_ci "rx-truncated-frames", 6262306a36Sopenharmony_ci "ethernet-bus-error", 6362306a36Sopenharmony_ci "tx-babbling-errors", 6462306a36Sopenharmony_ci "tx-underrun-errors", 6562306a36Sopenharmony_ci "tx-timeout-errors", 6662306a36Sopenharmony_ci /* rmon stats */ 6762306a36Sopenharmony_ci "tx-rx-64-frames", 6862306a36Sopenharmony_ci "tx-rx-65-127-frames", 6962306a36Sopenharmony_ci "tx-rx-128-255-frames", 7062306a36Sopenharmony_ci "tx-rx-256-511-frames", 7162306a36Sopenharmony_ci "tx-rx-512-1023-frames", 7262306a36Sopenharmony_ci "tx-rx-1024-1518-frames", 7362306a36Sopenharmony_ci "tx-rx-1519-1522-good-vlan", 7462306a36Sopenharmony_ci "rx-bytes", 7562306a36Sopenharmony_ci "rx-packets", 7662306a36Sopenharmony_ci "rx-fcs-errors", 7762306a36Sopenharmony_ci "receive-multicast-packet", 7862306a36Sopenharmony_ci "receive-broadcast-packet", 7962306a36Sopenharmony_ci "rx-control-frame-packets", 8062306a36Sopenharmony_ci "rx-pause-frame-packets", 8162306a36Sopenharmony_ci "rx-unknown-op-code", 8262306a36Sopenharmony_ci "rx-alignment-error", 8362306a36Sopenharmony_ci "rx-frame-length-error", 8462306a36Sopenharmony_ci "rx-code-error", 8562306a36Sopenharmony_ci "rx-carrier-sense-error", 8662306a36Sopenharmony_ci "rx-undersize-packets", 8762306a36Sopenharmony_ci "rx-oversize-packets", 8862306a36Sopenharmony_ci "rx-fragmented-frames", 8962306a36Sopenharmony_ci "rx-jabber-frames", 9062306a36Sopenharmony_ci "rx-dropped-frames", 9162306a36Sopenharmony_ci "tx-byte-counter", 9262306a36Sopenharmony_ci "tx-packets", 9362306a36Sopenharmony_ci "tx-multicast-packets", 9462306a36Sopenharmony_ci "tx-broadcast-packets", 9562306a36Sopenharmony_ci "tx-pause-control-frames", 9662306a36Sopenharmony_ci "tx-deferral-packets", 9762306a36Sopenharmony_ci "tx-excessive-deferral-packets", 9862306a36Sopenharmony_ci "tx-single-collision-packets", 9962306a36Sopenharmony_ci "tx-multiple-collision-packets", 10062306a36Sopenharmony_ci "tx-late-collision-packets", 10162306a36Sopenharmony_ci "tx-excessive-collision-packets", 10262306a36Sopenharmony_ci "tx-total-collision", 10362306a36Sopenharmony_ci "reserved", 10462306a36Sopenharmony_ci "tx-dropped-frames", 10562306a36Sopenharmony_ci "tx-jabber-frames", 10662306a36Sopenharmony_ci "tx-fcs-errors", 10762306a36Sopenharmony_ci "tx-control-frames", 10862306a36Sopenharmony_ci "tx-oversize-frames", 10962306a36Sopenharmony_ci "tx-undersize-frames", 11062306a36Sopenharmony_ci "tx-fragmented-frames", 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Fill in a buffer with the strings which correspond to the 11462306a36Sopenharmony_ci * stats */ 11562306a36Sopenharmony_cistatic void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) 12062306a36Sopenharmony_ci memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN); 12162306a36Sopenharmony_ci else 12262306a36Sopenharmony_ci memcpy(buf, stat_gstrings, 12362306a36Sopenharmony_ci GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* Fill in an array of 64-bit statistics from various sources. 12762306a36Sopenharmony_ci * This array will be appended to the end of the ethtool_stats 12862306a36Sopenharmony_ci * structure, and returned to user space 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, 13162306a36Sopenharmony_ci u64 *buf) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci int i; 13462306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 13562306a36Sopenharmony_ci struct gfar __iomem *regs = priv->gfargrp[0].regs; 13662306a36Sopenharmony_ci atomic64_t *extra = (atomic64_t *)&priv->extra_stats; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) 13962306a36Sopenharmony_ci buf[i] = atomic64_read(&extra[i]); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) { 14262306a36Sopenharmony_ci u32 __iomem *rmon = (u32 __iomem *) ®s->rmon; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci for (; i < GFAR_STATS_LEN; i++, rmon++) 14562306a36Sopenharmony_ci buf[i] = (u64) gfar_read(rmon); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int gfar_sset_count(struct net_device *dev, int sset) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci switch (sset) { 15462306a36Sopenharmony_ci case ETH_SS_STATS: 15562306a36Sopenharmony_ci if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) 15662306a36Sopenharmony_ci return GFAR_STATS_LEN; 15762306a36Sopenharmony_ci else 15862306a36Sopenharmony_ci return GFAR_EXTRA_STATS_LEN; 15962306a36Sopenharmony_ci default: 16062306a36Sopenharmony_ci return -EOPNOTSUPP; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* Fills in the drvinfo structure with some basic info */ 16562306a36Sopenharmony_cistatic void gfar_gdrvinfo(struct net_device *dev, 16662306a36Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* Return the length of the register structure */ 17262306a36Sopenharmony_cistatic int gfar_reglen(struct net_device *dev) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci return sizeof (struct gfar); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* Return a dump of the GFAR register space */ 17862306a36Sopenharmony_cistatic void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, 17962306a36Sopenharmony_ci void *regbuf) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci int i; 18262306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 18362306a36Sopenharmony_ci u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp[0].regs; 18462306a36Sopenharmony_ci u32 *buf = (u32 *) regbuf; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++) 18762306a36Sopenharmony_ci buf[i] = gfar_read(&theregs[i]); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* Convert microseconds to ethernet clock ticks, which changes 19162306a36Sopenharmony_ci * depending on what speed the controller is running at */ 19262306a36Sopenharmony_cistatic unsigned int gfar_usecs2ticks(struct gfar_private *priv, 19362306a36Sopenharmony_ci unsigned int usecs) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct net_device *ndev = priv->ndev; 19662306a36Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 19762306a36Sopenharmony_ci unsigned int count; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* The timer is different, depending on the interface speed */ 20062306a36Sopenharmony_ci switch (phydev->speed) { 20162306a36Sopenharmony_ci case SPEED_1000: 20262306a36Sopenharmony_ci count = GFAR_GBIT_TIME; 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci case SPEED_100: 20562306a36Sopenharmony_ci count = GFAR_100_TIME; 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci case SPEED_10: 20862306a36Sopenharmony_ci default: 20962306a36Sopenharmony_ci count = GFAR_10_TIME; 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Make sure we return a number greater than 0 21462306a36Sopenharmony_ci * if usecs > 0 */ 21562306a36Sopenharmony_ci return DIV_ROUND_UP(usecs * 1000, count); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* Convert ethernet clock ticks to microseconds */ 21962306a36Sopenharmony_cistatic unsigned int gfar_ticks2usecs(struct gfar_private *priv, 22062306a36Sopenharmony_ci unsigned int ticks) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct net_device *ndev = priv->ndev; 22362306a36Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 22462306a36Sopenharmony_ci unsigned int count; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* The timer is different, depending on the interface speed */ 22762306a36Sopenharmony_ci switch (phydev->speed) { 22862306a36Sopenharmony_ci case SPEED_1000: 22962306a36Sopenharmony_ci count = GFAR_GBIT_TIME; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case SPEED_100: 23262306a36Sopenharmony_ci count = GFAR_100_TIME; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci case SPEED_10: 23562306a36Sopenharmony_ci default: 23662306a36Sopenharmony_ci count = GFAR_10_TIME; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Make sure we return a number greater than 0 */ 24162306a36Sopenharmony_ci /* if ticks is > 0 */ 24262306a36Sopenharmony_ci return (ticks * count) / 1000; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* Get the coalescing parameters, and put them in the cvals 24662306a36Sopenharmony_ci * structure. */ 24762306a36Sopenharmony_cistatic int gfar_gcoalesce(struct net_device *dev, 24862306a36Sopenharmony_ci struct ethtool_coalesce *cvals, 24962306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 25062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 25362306a36Sopenharmony_ci struct gfar_priv_rx_q *rx_queue = NULL; 25462306a36Sopenharmony_ci struct gfar_priv_tx_q *tx_queue = NULL; 25562306a36Sopenharmony_ci unsigned long rxtime; 25662306a36Sopenharmony_ci unsigned long rxcount; 25762306a36Sopenharmony_ci unsigned long txtime; 25862306a36Sopenharmony_ci unsigned long txcount; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) 26162306a36Sopenharmony_ci return -EOPNOTSUPP; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!dev->phydev) 26462306a36Sopenharmony_ci return -ENODEV; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci rx_queue = priv->rx_queue[0]; 26762306a36Sopenharmony_ci tx_queue = priv->tx_queue[0]; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci rxtime = get_ictt_value(rx_queue->rxic); 27062306a36Sopenharmony_ci rxcount = get_icft_value(rx_queue->rxic); 27162306a36Sopenharmony_ci txtime = get_ictt_value(tx_queue->txic); 27262306a36Sopenharmony_ci txcount = get_icft_value(tx_queue->txic); 27362306a36Sopenharmony_ci cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime); 27462306a36Sopenharmony_ci cvals->rx_max_coalesced_frames = rxcount; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, txtime); 27762306a36Sopenharmony_ci cvals->tx_max_coalesced_frames = txcount; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* Change the coalescing values. 28362306a36Sopenharmony_ci * Both cvals->*_usecs and cvals->*_frames have to be > 0 28462306a36Sopenharmony_ci * in order for coalescing to be active 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_cistatic int gfar_scoalesce(struct net_device *dev, 28762306a36Sopenharmony_ci struct ethtool_coalesce *cvals, 28862306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 28962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 29262306a36Sopenharmony_ci int i, err = 0; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) 29562306a36Sopenharmony_ci return -EOPNOTSUPP; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!dev->phydev) 29862306a36Sopenharmony_ci return -ENODEV; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Check the bounds of the values */ 30162306a36Sopenharmony_ci if (cvals->rx_coalesce_usecs > GFAR_MAX_COAL_USECS) { 30262306a36Sopenharmony_ci netdev_info(dev, "Coalescing is limited to %d microseconds\n", 30362306a36Sopenharmony_ci GFAR_MAX_COAL_USECS); 30462306a36Sopenharmony_ci return -EINVAL; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (cvals->rx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) { 30862306a36Sopenharmony_ci netdev_info(dev, "Coalescing is limited to %d frames\n", 30962306a36Sopenharmony_ci GFAR_MAX_COAL_FRAMES); 31062306a36Sopenharmony_ci return -EINVAL; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Check the bounds of the values */ 31462306a36Sopenharmony_ci if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) { 31562306a36Sopenharmony_ci netdev_info(dev, "Coalescing is limited to %d microseconds\n", 31662306a36Sopenharmony_ci GFAR_MAX_COAL_USECS); 31762306a36Sopenharmony_ci return -EINVAL; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) { 32162306a36Sopenharmony_ci netdev_info(dev, "Coalescing is limited to %d frames\n", 32262306a36Sopenharmony_ci GFAR_MAX_COAL_FRAMES); 32362306a36Sopenharmony_ci return -EINVAL; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) 32762306a36Sopenharmony_ci cpu_relax(); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* Set up rx coalescing */ 33062306a36Sopenharmony_ci if ((cvals->rx_coalesce_usecs == 0) || 33162306a36Sopenharmony_ci (cvals->rx_max_coalesced_frames == 0)) { 33262306a36Sopenharmony_ci for (i = 0; i < priv->num_rx_queues; i++) 33362306a36Sopenharmony_ci priv->rx_queue[i]->rxcoalescing = 0; 33462306a36Sopenharmony_ci } else { 33562306a36Sopenharmony_ci for (i = 0; i < priv->num_rx_queues; i++) 33662306a36Sopenharmony_ci priv->rx_queue[i]->rxcoalescing = 1; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (i = 0; i < priv->num_rx_queues; i++) { 34062306a36Sopenharmony_ci priv->rx_queue[i]->rxic = mk_ic_value( 34162306a36Sopenharmony_ci cvals->rx_max_coalesced_frames, 34262306a36Sopenharmony_ci gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs)); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Set up tx coalescing */ 34662306a36Sopenharmony_ci if ((cvals->tx_coalesce_usecs == 0) || 34762306a36Sopenharmony_ci (cvals->tx_max_coalesced_frames == 0)) { 34862306a36Sopenharmony_ci for (i = 0; i < priv->num_tx_queues; i++) 34962306a36Sopenharmony_ci priv->tx_queue[i]->txcoalescing = 0; 35062306a36Sopenharmony_ci } else { 35162306a36Sopenharmony_ci for (i = 0; i < priv->num_tx_queues; i++) 35262306a36Sopenharmony_ci priv->tx_queue[i]->txcoalescing = 1; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci for (i = 0; i < priv->num_tx_queues; i++) { 35662306a36Sopenharmony_ci priv->tx_queue[i]->txic = mk_ic_value( 35762306a36Sopenharmony_ci cvals->tx_max_coalesced_frames, 35862306a36Sopenharmony_ci gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs)); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (dev->flags & IFF_UP) { 36262306a36Sopenharmony_ci stop_gfar(dev); 36362306a36Sopenharmony_ci err = startup_gfar(dev); 36462306a36Sopenharmony_ci } else { 36562306a36Sopenharmony_ci gfar_mac_reset(priv); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci clear_bit_unlock(GFAR_RESETTING, &priv->state); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return err; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci/* Fills in rvals with the current ring parameters. Currently, 37462306a36Sopenharmony_ci * rx, rx_mini, and rx_jumbo rings are the same size, as mini and 37562306a36Sopenharmony_ci * jumbo are ignored by the driver */ 37662306a36Sopenharmony_cistatic void gfar_gringparam(struct net_device *dev, 37762306a36Sopenharmony_ci struct ethtool_ringparam *rvals, 37862306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_rvals, 37962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 38262306a36Sopenharmony_ci struct gfar_priv_tx_q *tx_queue = NULL; 38362306a36Sopenharmony_ci struct gfar_priv_rx_q *rx_queue = NULL; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci tx_queue = priv->tx_queue[0]; 38662306a36Sopenharmony_ci rx_queue = priv->rx_queue[0]; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE; 38962306a36Sopenharmony_ci rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE; 39062306a36Sopenharmony_ci rvals->rx_jumbo_max_pending = GFAR_RX_MAX_RING_SIZE; 39162306a36Sopenharmony_ci rvals->tx_max_pending = GFAR_TX_MAX_RING_SIZE; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* Values changeable by the user. The valid values are 39462306a36Sopenharmony_ci * in the range 1 to the "*_max_pending" counterpart above. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci rvals->rx_pending = rx_queue->rx_ring_size; 39762306a36Sopenharmony_ci rvals->rx_mini_pending = rx_queue->rx_ring_size; 39862306a36Sopenharmony_ci rvals->rx_jumbo_pending = rx_queue->rx_ring_size; 39962306a36Sopenharmony_ci rvals->tx_pending = tx_queue->tx_ring_size; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci/* Change the current ring parameters, stopping the controller if 40362306a36Sopenharmony_ci * necessary so that we don't mess things up while we're in motion. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_cistatic int gfar_sringparam(struct net_device *dev, 40662306a36Sopenharmony_ci struct ethtool_ringparam *rvals, 40762306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_rvals, 40862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 41162306a36Sopenharmony_ci int err = 0, i; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE) 41462306a36Sopenharmony_ci return -EINVAL; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (!is_power_of_2(rvals->rx_pending)) { 41762306a36Sopenharmony_ci netdev_err(dev, "Ring sizes must be a power of 2\n"); 41862306a36Sopenharmony_ci return -EINVAL; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (rvals->tx_pending > GFAR_TX_MAX_RING_SIZE) 42262306a36Sopenharmony_ci return -EINVAL; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (!is_power_of_2(rvals->tx_pending)) { 42562306a36Sopenharmony_ci netdev_err(dev, "Ring sizes must be a power of 2\n"); 42662306a36Sopenharmony_ci return -EINVAL; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) 43062306a36Sopenharmony_ci cpu_relax(); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (dev->flags & IFF_UP) 43362306a36Sopenharmony_ci stop_gfar(dev); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Change the sizes */ 43662306a36Sopenharmony_ci for (i = 0; i < priv->num_rx_queues; i++) 43762306a36Sopenharmony_ci priv->rx_queue[i]->rx_ring_size = rvals->rx_pending; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci for (i = 0; i < priv->num_tx_queues; i++) 44062306a36Sopenharmony_ci priv->tx_queue[i]->tx_ring_size = rvals->tx_pending; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Rebuild the rings with the new size */ 44362306a36Sopenharmony_ci if (dev->flags & IFF_UP) 44462306a36Sopenharmony_ci err = startup_gfar(dev); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci clear_bit_unlock(GFAR_RESETTING, &priv->state); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void gfar_gpauseparam(struct net_device *dev, 45262306a36Sopenharmony_ci struct ethtool_pauseparam *epause) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci epause->autoneg = !!priv->pause_aneg_en; 45762306a36Sopenharmony_ci epause->rx_pause = !!priv->rx_pause_en; 45862306a36Sopenharmony_ci epause->tx_pause = !!priv->tx_pause_en; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int gfar_spauseparam(struct net_device *dev, 46262306a36Sopenharmony_ci struct ethtool_pauseparam *epause) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 46562306a36Sopenharmony_ci struct phy_device *phydev = dev->phydev; 46662306a36Sopenharmony_ci struct gfar __iomem *regs = priv->gfargrp[0].regs; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (!phydev) 46962306a36Sopenharmony_ci return -ENODEV; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (!phy_validate_pause(phydev, epause)) 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci priv->rx_pause_en = priv->tx_pause_en = 0; 47562306a36Sopenharmony_ci phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause); 47662306a36Sopenharmony_ci if (epause->rx_pause) { 47762306a36Sopenharmony_ci priv->rx_pause_en = 1; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (epause->tx_pause) { 48062306a36Sopenharmony_ci priv->tx_pause_en = 1; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci } else if (epause->tx_pause) { 48362306a36Sopenharmony_ci priv->tx_pause_en = 1; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (epause->autoneg) 48762306a36Sopenharmony_ci priv->pause_aneg_en = 1; 48862306a36Sopenharmony_ci else 48962306a36Sopenharmony_ci priv->pause_aneg_en = 0; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (!epause->autoneg) { 49262306a36Sopenharmony_ci u32 tempval = gfar_read(®s->maccfg1); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci priv->tx_actual_en = 0; 49762306a36Sopenharmony_ci if (priv->tx_pause_en) { 49862306a36Sopenharmony_ci priv->tx_actual_en = 1; 49962306a36Sopenharmony_ci tempval |= MACCFG1_TX_FLOW; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (priv->rx_pause_en) 50362306a36Sopenharmony_ci tempval |= MACCFG1_RX_FLOW; 50462306a36Sopenharmony_ci gfar_write(®s->maccfg1, tempval); 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ciint gfar_set_features(struct net_device *dev, netdev_features_t features) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci netdev_features_t changed = dev->features ^ features; 51362306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 51462306a36Sopenharmony_ci int err = 0; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | 51762306a36Sopenharmony_ci NETIF_F_RXCSUM))) 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) 52162306a36Sopenharmony_ci cpu_relax(); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci dev->features = features; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (dev->flags & IFF_UP) { 52662306a36Sopenharmony_ci /* Now we take down the rings to rebuild them */ 52762306a36Sopenharmony_ci stop_gfar(dev); 52862306a36Sopenharmony_ci err = startup_gfar(dev); 52962306a36Sopenharmony_ci } else { 53062306a36Sopenharmony_ci gfar_mac_reset(priv); 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci clear_bit_unlock(GFAR_RESETTING, &priv->state); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return err; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic uint32_t gfar_get_msglevel(struct net_device *dev) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return priv->msg_enable; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic void gfar_set_msglevel(struct net_device *dev, uint32_t data) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci priv->msg_enable = data; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci#ifdef CONFIG_PM 55362306a36Sopenharmony_cistatic void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci wol->supported = 0; 55862306a36Sopenharmony_ci wol->wolopts = 0; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (priv->wol_supported & GFAR_WOL_MAGIC) 56162306a36Sopenharmony_ci wol->supported |= WAKE_MAGIC; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (priv->wol_supported & GFAR_WOL_FILER_UCAST) 56462306a36Sopenharmony_ci wol->supported |= WAKE_UCAST; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (priv->wol_opts & GFAR_WOL_MAGIC) 56762306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (priv->wol_opts & GFAR_WOL_FILER_UCAST) 57062306a36Sopenharmony_ci wol->wolopts |= WAKE_UCAST; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 57662306a36Sopenharmony_ci u16 wol_opts = 0; 57762306a36Sopenharmony_ci int err; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!priv->wol_supported && wol->wolopts) 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (wol->wolopts & ~(WAKE_MAGIC | WAKE_UCAST)) 58362306a36Sopenharmony_ci return -EINVAL; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) { 58662306a36Sopenharmony_ci wol_opts |= GFAR_WOL_MAGIC; 58762306a36Sopenharmony_ci } else { 58862306a36Sopenharmony_ci if (wol->wolopts & WAKE_UCAST) 58962306a36Sopenharmony_ci wol_opts |= GFAR_WOL_FILER_UCAST; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci wol_opts &= priv->wol_supported; 59362306a36Sopenharmony_ci priv->wol_opts = 0; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci err = device_set_wakeup_enable(priv->dev, wol_opts); 59662306a36Sopenharmony_ci if (err) 59762306a36Sopenharmony_ci return err; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci priv->wol_opts = wol_opts; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci#endif 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci u32 fcr = 0x0, fpr = FPR_FILER_MASK; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (ethflow & RXH_L2DA) { 61062306a36Sopenharmony_ci fcr = RQFCR_PID_DAH | RQFCR_CMP_NOMATCH | 61162306a36Sopenharmony_ci RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; 61262306a36Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 61362306a36Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 61462306a36Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 61562306a36Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci fcr = RQFCR_PID_DAL | RQFCR_CMP_NOMATCH | 61862306a36Sopenharmony_ci RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; 61962306a36Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 62062306a36Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 62162306a36Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 62262306a36Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (ethflow & RXH_VLAN) { 62662306a36Sopenharmony_ci fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH | 62762306a36Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 62862306a36Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 62962306a36Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 63062306a36Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 63162306a36Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (ethflow & RXH_IP_SRC) { 63562306a36Sopenharmony_ci fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | 63662306a36Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 63762306a36Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 63862306a36Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 63962306a36Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 64062306a36Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (ethflow & (RXH_IP_DST)) { 64462306a36Sopenharmony_ci fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | 64562306a36Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 64662306a36Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 64762306a36Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 64862306a36Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 64962306a36Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (ethflow & RXH_L3_PROTO) { 65362306a36Sopenharmony_ci fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH | 65462306a36Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 65562306a36Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 65662306a36Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 65762306a36Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 65862306a36Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (ethflow & RXH_L4_B_0_1) { 66262306a36Sopenharmony_ci fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | 66362306a36Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 66462306a36Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 66562306a36Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 66662306a36Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 66762306a36Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (ethflow & RXH_L4_B_2_3) { 67162306a36Sopenharmony_ci fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | 67262306a36Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 67362306a36Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 67462306a36Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 67562306a36Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 67662306a36Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, 68162306a36Sopenharmony_ci u64 class) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci unsigned int cmp_rqfpr; 68462306a36Sopenharmony_ci unsigned int *local_rqfpr; 68562306a36Sopenharmony_ci unsigned int *local_rqfcr; 68662306a36Sopenharmony_ci int i = 0x0, k = 0x0; 68762306a36Sopenharmony_ci int j = MAX_FILER_IDX, l = 0x0; 68862306a36Sopenharmony_ci int ret = 1; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci local_rqfpr = kmalloc_array(MAX_FILER_IDX + 1, sizeof(unsigned int), 69162306a36Sopenharmony_ci GFP_KERNEL); 69262306a36Sopenharmony_ci local_rqfcr = kmalloc_array(MAX_FILER_IDX + 1, sizeof(unsigned int), 69362306a36Sopenharmony_ci GFP_KERNEL); 69462306a36Sopenharmony_ci if (!local_rqfpr || !local_rqfcr) { 69562306a36Sopenharmony_ci ret = 0; 69662306a36Sopenharmony_ci goto err; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci switch (class) { 70062306a36Sopenharmony_ci case TCP_V4_FLOW: 70162306a36Sopenharmony_ci cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP; 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci case UDP_V4_FLOW: 70462306a36Sopenharmony_ci cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP; 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci case TCP_V6_FLOW: 70762306a36Sopenharmony_ci cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP; 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci case UDP_V6_FLOW: 71062306a36Sopenharmony_ci cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP; 71162306a36Sopenharmony_ci break; 71262306a36Sopenharmony_ci default: 71362306a36Sopenharmony_ci netdev_err(priv->ndev, 71462306a36Sopenharmony_ci "Right now this class is not supported\n"); 71562306a36Sopenharmony_ci ret = 0; 71662306a36Sopenharmony_ci goto err; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci for (i = 0; i < MAX_FILER_IDX + 1; i++) { 72062306a36Sopenharmony_ci local_rqfpr[j] = priv->ftp_rqfpr[i]; 72162306a36Sopenharmony_ci local_rqfcr[j] = priv->ftp_rqfcr[i]; 72262306a36Sopenharmony_ci j--; 72362306a36Sopenharmony_ci if ((priv->ftp_rqfcr[i] == 72462306a36Sopenharmony_ci (RQFCR_PID_PARSE | RQFCR_CLE | RQFCR_AND)) && 72562306a36Sopenharmony_ci (priv->ftp_rqfpr[i] == cmp_rqfpr)) 72662306a36Sopenharmony_ci break; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (i == MAX_FILER_IDX + 1) { 73062306a36Sopenharmony_ci netdev_err(priv->ndev, 73162306a36Sopenharmony_ci "No parse rule found, can't create hash rules\n"); 73262306a36Sopenharmony_ci ret = 0; 73362306a36Sopenharmony_ci goto err; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* If a match was found, then it begins the starting of a cluster rule 73762306a36Sopenharmony_ci * if it was already programmed, we need to overwrite these rules 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_ci for (l = i+1; l < MAX_FILER_IDX; l++) { 74062306a36Sopenharmony_ci if ((priv->ftp_rqfcr[l] & RQFCR_CLE) && 74162306a36Sopenharmony_ci !(priv->ftp_rqfcr[l] & RQFCR_AND)) { 74262306a36Sopenharmony_ci priv->ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT | 74362306a36Sopenharmony_ci RQFCR_HASHTBL_0 | RQFCR_PID_MASK; 74462306a36Sopenharmony_ci priv->ftp_rqfpr[l] = FPR_FILER_MASK; 74562306a36Sopenharmony_ci gfar_write_filer(priv, l, priv->ftp_rqfcr[l], 74662306a36Sopenharmony_ci priv->ftp_rqfpr[l]); 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (!(priv->ftp_rqfcr[l] & RQFCR_CLE) && 75162306a36Sopenharmony_ci (priv->ftp_rqfcr[l] & RQFCR_AND)) 75262306a36Sopenharmony_ci continue; 75362306a36Sopenharmony_ci else { 75462306a36Sopenharmony_ci local_rqfpr[j] = priv->ftp_rqfpr[l]; 75562306a36Sopenharmony_ci local_rqfcr[j] = priv->ftp_rqfcr[l]; 75662306a36Sopenharmony_ci j--; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci priv->cur_filer_idx = l - 1; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* hash rules */ 76362306a36Sopenharmony_ci ethflow_to_filer_rules(priv, ethflow); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* Write back the popped out rules again */ 76662306a36Sopenharmony_ci for (k = j+1; k < MAX_FILER_IDX; k++) { 76762306a36Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k]; 76862306a36Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k]; 76962306a36Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, 77062306a36Sopenharmony_ci local_rqfcr[k], local_rqfpr[k]); 77162306a36Sopenharmony_ci if (!priv->cur_filer_idx) 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cierr: 77762306a36Sopenharmony_ci kfree(local_rqfcr); 77862306a36Sopenharmony_ci kfree(local_rqfpr); 77962306a36Sopenharmony_ci return ret; 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic int gfar_set_hash_opts(struct gfar_private *priv, 78362306a36Sopenharmony_ci struct ethtool_rxnfc *cmd) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci /* write the filer rules here */ 78662306a36Sopenharmony_ci if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type)) 78762306a36Sopenharmony_ci return -EINVAL; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci return 0; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic int gfar_check_filer_hardware(struct gfar_private *priv) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct gfar __iomem *regs = priv->gfargrp[0].regs; 79562306a36Sopenharmony_ci u32 i; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* Check if we are in FIFO mode */ 79862306a36Sopenharmony_ci i = gfar_read(®s->ecntrl); 79962306a36Sopenharmony_ci i &= ECNTRL_FIFM; 80062306a36Sopenharmony_ci if (i == ECNTRL_FIFM) { 80162306a36Sopenharmony_ci netdev_notice(priv->ndev, "Interface in FIFO mode\n"); 80262306a36Sopenharmony_ci i = gfar_read(®s->rctrl); 80362306a36Sopenharmony_ci i &= RCTRL_PRSDEP_MASK | RCTRL_PRSFM; 80462306a36Sopenharmony_ci if (i == (RCTRL_PRSDEP_MASK | RCTRL_PRSFM)) { 80562306a36Sopenharmony_ci netdev_info(priv->ndev, 80662306a36Sopenharmony_ci "Receive Queue Filtering enabled\n"); 80762306a36Sopenharmony_ci } else { 80862306a36Sopenharmony_ci netdev_warn(priv->ndev, 80962306a36Sopenharmony_ci "Receive Queue Filtering disabled\n"); 81062306a36Sopenharmony_ci return -EOPNOTSUPP; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci /* Or in standard mode */ 81462306a36Sopenharmony_ci else { 81562306a36Sopenharmony_ci i = gfar_read(®s->rctrl); 81662306a36Sopenharmony_ci i &= RCTRL_PRSDEP_MASK; 81762306a36Sopenharmony_ci if (i == RCTRL_PRSDEP_MASK) { 81862306a36Sopenharmony_ci netdev_info(priv->ndev, 81962306a36Sopenharmony_ci "Receive Queue Filtering enabled\n"); 82062306a36Sopenharmony_ci } else { 82162306a36Sopenharmony_ci netdev_warn(priv->ndev, 82262306a36Sopenharmony_ci "Receive Queue Filtering disabled\n"); 82362306a36Sopenharmony_ci return -EOPNOTSUPP; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* Sets the properties for arbitrary filer rule 82862306a36Sopenharmony_ci * to the first 4 Layer 4 Bytes 82962306a36Sopenharmony_ci */ 83062306a36Sopenharmony_ci gfar_write(®s->rbifx, 0xC0C1C2C3); 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci/* Write a mask to filer cache */ 83562306a36Sopenharmony_cistatic void gfar_set_mask(u32 mask, struct filer_table *tab) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci tab->fe[tab->index].ctrl = RQFCR_AND | RQFCR_PID_MASK | RQFCR_CMP_EXACT; 83862306a36Sopenharmony_ci tab->fe[tab->index].prop = mask; 83962306a36Sopenharmony_ci tab->index++; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/* Sets parse bits (e.g. IP or TCP) */ 84362306a36Sopenharmony_cistatic void gfar_set_parse_bits(u32 value, u32 mask, struct filer_table *tab) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci gfar_set_mask(mask, tab); 84662306a36Sopenharmony_ci tab->fe[tab->index].ctrl = RQFCR_CMP_EXACT | RQFCR_PID_PARSE | 84762306a36Sopenharmony_ci RQFCR_AND; 84862306a36Sopenharmony_ci tab->fe[tab->index].prop = value; 84962306a36Sopenharmony_ci tab->index++; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic void gfar_set_general_attribute(u32 value, u32 mask, u32 flag, 85362306a36Sopenharmony_ci struct filer_table *tab) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci gfar_set_mask(mask, tab); 85662306a36Sopenharmony_ci tab->fe[tab->index].ctrl = RQFCR_CMP_EXACT | RQFCR_AND | flag; 85762306a36Sopenharmony_ci tab->fe[tab->index].prop = value; 85862306a36Sopenharmony_ci tab->index++; 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci/* For setting a tuple of value and mask of type flag 86262306a36Sopenharmony_ci * Example: 86362306a36Sopenharmony_ci * IP-Src = 10.0.0.0/255.0.0.0 86462306a36Sopenharmony_ci * value: 0x0A000000 mask: FF000000 flag: RQFPR_IPV4 86562306a36Sopenharmony_ci * 86662306a36Sopenharmony_ci * Ethtool gives us a value=0 and mask=~0 for don't care a tuple 86762306a36Sopenharmony_ci * For a don't care mask it gives us a 0 86862306a36Sopenharmony_ci * 86962306a36Sopenharmony_ci * The check if don't care and the mask adjustment if mask=0 is done for VLAN 87062306a36Sopenharmony_ci * and MAC stuff on an upper level (due to missing information on this level). 87162306a36Sopenharmony_ci * For these guys we can discard them if they are value=0 and mask=0. 87262306a36Sopenharmony_ci * 87362306a36Sopenharmony_ci * Further the all masks are one-padded for better hardware efficiency. 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_cistatic void gfar_set_attribute(u32 value, u32 mask, u32 flag, 87662306a36Sopenharmony_ci struct filer_table *tab) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci switch (flag) { 87962306a36Sopenharmony_ci /* 3bit */ 88062306a36Sopenharmony_ci case RQFCR_PID_PRI: 88162306a36Sopenharmony_ci if (!(value | mask)) 88262306a36Sopenharmony_ci return; 88362306a36Sopenharmony_ci mask |= RQFCR_PID_PRI_MASK; 88462306a36Sopenharmony_ci break; 88562306a36Sopenharmony_ci /* 8bit */ 88662306a36Sopenharmony_ci case RQFCR_PID_L4P: 88762306a36Sopenharmony_ci case RQFCR_PID_TOS: 88862306a36Sopenharmony_ci if (!~(mask | RQFCR_PID_L4P_MASK)) 88962306a36Sopenharmony_ci return; 89062306a36Sopenharmony_ci if (!mask) 89162306a36Sopenharmony_ci mask = ~0; 89262306a36Sopenharmony_ci else 89362306a36Sopenharmony_ci mask |= RQFCR_PID_L4P_MASK; 89462306a36Sopenharmony_ci break; 89562306a36Sopenharmony_ci /* 12bit */ 89662306a36Sopenharmony_ci case RQFCR_PID_VID: 89762306a36Sopenharmony_ci if (!(value | mask)) 89862306a36Sopenharmony_ci return; 89962306a36Sopenharmony_ci mask |= RQFCR_PID_VID_MASK; 90062306a36Sopenharmony_ci break; 90162306a36Sopenharmony_ci /* 16bit */ 90262306a36Sopenharmony_ci case RQFCR_PID_DPT: 90362306a36Sopenharmony_ci case RQFCR_PID_SPT: 90462306a36Sopenharmony_ci case RQFCR_PID_ETY: 90562306a36Sopenharmony_ci if (!~(mask | RQFCR_PID_PORT_MASK)) 90662306a36Sopenharmony_ci return; 90762306a36Sopenharmony_ci if (!mask) 90862306a36Sopenharmony_ci mask = ~0; 90962306a36Sopenharmony_ci else 91062306a36Sopenharmony_ci mask |= RQFCR_PID_PORT_MASK; 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci /* 24bit */ 91362306a36Sopenharmony_ci case RQFCR_PID_DAH: 91462306a36Sopenharmony_ci case RQFCR_PID_DAL: 91562306a36Sopenharmony_ci case RQFCR_PID_SAH: 91662306a36Sopenharmony_ci case RQFCR_PID_SAL: 91762306a36Sopenharmony_ci if (!(value | mask)) 91862306a36Sopenharmony_ci return; 91962306a36Sopenharmony_ci mask |= RQFCR_PID_MAC_MASK; 92062306a36Sopenharmony_ci break; 92162306a36Sopenharmony_ci /* for all real 32bit masks */ 92262306a36Sopenharmony_ci default: 92362306a36Sopenharmony_ci if (!~mask) 92462306a36Sopenharmony_ci return; 92562306a36Sopenharmony_ci if (!mask) 92662306a36Sopenharmony_ci mask = ~0; 92762306a36Sopenharmony_ci break; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci gfar_set_general_attribute(value, mask, flag, tab); 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci/* Translates value and mask for UDP, TCP or SCTP */ 93362306a36Sopenharmony_cistatic void gfar_set_basic_ip(struct ethtool_tcpip4_spec *value, 93462306a36Sopenharmony_ci struct ethtool_tcpip4_spec *mask, 93562306a36Sopenharmony_ci struct filer_table *tab) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->ip4src), 93862306a36Sopenharmony_ci be32_to_cpu(mask->ip4src), 93962306a36Sopenharmony_ci RQFCR_PID_SIA, tab); 94062306a36Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->ip4dst), 94162306a36Sopenharmony_ci be32_to_cpu(mask->ip4dst), 94262306a36Sopenharmony_ci RQFCR_PID_DIA, tab); 94362306a36Sopenharmony_ci gfar_set_attribute(be16_to_cpu(value->pdst), 94462306a36Sopenharmony_ci be16_to_cpu(mask->pdst), 94562306a36Sopenharmony_ci RQFCR_PID_DPT, tab); 94662306a36Sopenharmony_ci gfar_set_attribute(be16_to_cpu(value->psrc), 94762306a36Sopenharmony_ci be16_to_cpu(mask->psrc), 94862306a36Sopenharmony_ci RQFCR_PID_SPT, tab); 94962306a36Sopenharmony_ci gfar_set_attribute(value->tos, mask->tos, RQFCR_PID_TOS, tab); 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci/* Translates value and mask for RAW-IP4 */ 95362306a36Sopenharmony_cistatic void gfar_set_user_ip(struct ethtool_usrip4_spec *value, 95462306a36Sopenharmony_ci struct ethtool_usrip4_spec *mask, 95562306a36Sopenharmony_ci struct filer_table *tab) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->ip4src), 95862306a36Sopenharmony_ci be32_to_cpu(mask->ip4src), 95962306a36Sopenharmony_ci RQFCR_PID_SIA, tab); 96062306a36Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->ip4dst), 96162306a36Sopenharmony_ci be32_to_cpu(mask->ip4dst), 96262306a36Sopenharmony_ci RQFCR_PID_DIA, tab); 96362306a36Sopenharmony_ci gfar_set_attribute(value->tos, mask->tos, RQFCR_PID_TOS, tab); 96462306a36Sopenharmony_ci gfar_set_attribute(value->proto, mask->proto, RQFCR_PID_L4P, tab); 96562306a36Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->l4_4_bytes), 96662306a36Sopenharmony_ci be32_to_cpu(mask->l4_4_bytes), 96762306a36Sopenharmony_ci RQFCR_PID_ARB, tab); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci/* Translates value and mask for ETHER spec */ 97262306a36Sopenharmony_cistatic void gfar_set_ether(struct ethhdr *value, struct ethhdr *mask, 97362306a36Sopenharmony_ci struct filer_table *tab) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci u32 upper_temp_mask = 0; 97662306a36Sopenharmony_ci u32 lower_temp_mask = 0; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci /* Source address */ 97962306a36Sopenharmony_ci if (!is_broadcast_ether_addr(mask->h_source)) { 98062306a36Sopenharmony_ci if (is_zero_ether_addr(mask->h_source)) { 98162306a36Sopenharmony_ci upper_temp_mask = 0xFFFFFFFF; 98262306a36Sopenharmony_ci lower_temp_mask = 0xFFFFFFFF; 98362306a36Sopenharmony_ci } else { 98462306a36Sopenharmony_ci upper_temp_mask = mask->h_source[0] << 16 | 98562306a36Sopenharmony_ci mask->h_source[1] << 8 | 98662306a36Sopenharmony_ci mask->h_source[2]; 98762306a36Sopenharmony_ci lower_temp_mask = mask->h_source[3] << 16 | 98862306a36Sopenharmony_ci mask->h_source[4] << 8 | 98962306a36Sopenharmony_ci mask->h_source[5]; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci /* Upper 24bit */ 99262306a36Sopenharmony_ci gfar_set_attribute(value->h_source[0] << 16 | 99362306a36Sopenharmony_ci value->h_source[1] << 8 | 99462306a36Sopenharmony_ci value->h_source[2], 99562306a36Sopenharmony_ci upper_temp_mask, RQFCR_PID_SAH, tab); 99662306a36Sopenharmony_ci /* And the same for the lower part */ 99762306a36Sopenharmony_ci gfar_set_attribute(value->h_source[3] << 16 | 99862306a36Sopenharmony_ci value->h_source[4] << 8 | 99962306a36Sopenharmony_ci value->h_source[5], 100062306a36Sopenharmony_ci lower_temp_mask, RQFCR_PID_SAL, tab); 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci /* Destination address */ 100362306a36Sopenharmony_ci if (!is_broadcast_ether_addr(mask->h_dest)) { 100462306a36Sopenharmony_ci /* Special for destination is limited broadcast */ 100562306a36Sopenharmony_ci if ((is_broadcast_ether_addr(value->h_dest) && 100662306a36Sopenharmony_ci is_zero_ether_addr(mask->h_dest))) { 100762306a36Sopenharmony_ci gfar_set_parse_bits(RQFPR_EBC, RQFPR_EBC, tab); 100862306a36Sopenharmony_ci } else { 100962306a36Sopenharmony_ci if (is_zero_ether_addr(mask->h_dest)) { 101062306a36Sopenharmony_ci upper_temp_mask = 0xFFFFFFFF; 101162306a36Sopenharmony_ci lower_temp_mask = 0xFFFFFFFF; 101262306a36Sopenharmony_ci } else { 101362306a36Sopenharmony_ci upper_temp_mask = mask->h_dest[0] << 16 | 101462306a36Sopenharmony_ci mask->h_dest[1] << 8 | 101562306a36Sopenharmony_ci mask->h_dest[2]; 101662306a36Sopenharmony_ci lower_temp_mask = mask->h_dest[3] << 16 | 101762306a36Sopenharmony_ci mask->h_dest[4] << 8 | 101862306a36Sopenharmony_ci mask->h_dest[5]; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* Upper 24bit */ 102262306a36Sopenharmony_ci gfar_set_attribute(value->h_dest[0] << 16 | 102362306a36Sopenharmony_ci value->h_dest[1] << 8 | 102462306a36Sopenharmony_ci value->h_dest[2], 102562306a36Sopenharmony_ci upper_temp_mask, RQFCR_PID_DAH, tab); 102662306a36Sopenharmony_ci /* And the same for the lower part */ 102762306a36Sopenharmony_ci gfar_set_attribute(value->h_dest[3] << 16 | 102862306a36Sopenharmony_ci value->h_dest[4] << 8 | 102962306a36Sopenharmony_ci value->h_dest[5], 103062306a36Sopenharmony_ci lower_temp_mask, RQFCR_PID_DAL, tab); 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci gfar_set_attribute(be16_to_cpu(value->h_proto), 103562306a36Sopenharmony_ci be16_to_cpu(mask->h_proto), 103662306a36Sopenharmony_ci RQFCR_PID_ETY, tab); 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic inline u32 vlan_tci_vid(struct ethtool_rx_flow_spec *rule) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci return be16_to_cpu(rule->h_ext.vlan_tci) & VLAN_VID_MASK; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic inline u32 vlan_tci_vidm(struct ethtool_rx_flow_spec *rule) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci return be16_to_cpu(rule->m_ext.vlan_tci) & VLAN_VID_MASK; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic inline u32 vlan_tci_cfi(struct ethtool_rx_flow_spec *rule) 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci return be16_to_cpu(rule->h_ext.vlan_tci) & VLAN_CFI_MASK; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic inline u32 vlan_tci_cfim(struct ethtool_rx_flow_spec *rule) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci return be16_to_cpu(rule->m_ext.vlan_tci) & VLAN_CFI_MASK; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic inline u32 vlan_tci_prio(struct ethtool_rx_flow_spec *rule) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci return (be16_to_cpu(rule->h_ext.vlan_tci) & VLAN_PRIO_MASK) >> 106262306a36Sopenharmony_ci VLAN_PRIO_SHIFT; 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic inline u32 vlan_tci_priom(struct ethtool_rx_flow_spec *rule) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci return (be16_to_cpu(rule->m_ext.vlan_tci) & VLAN_PRIO_MASK) >> 106862306a36Sopenharmony_ci VLAN_PRIO_SHIFT; 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci/* Convert a rule to binary filter format of gianfar */ 107262306a36Sopenharmony_cistatic int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule, 107362306a36Sopenharmony_ci struct filer_table *tab) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci u32 vlan = 0, vlan_mask = 0; 107662306a36Sopenharmony_ci u32 id = 0, id_mask = 0; 107762306a36Sopenharmony_ci u32 cfi = 0, cfi_mask = 0; 107862306a36Sopenharmony_ci u32 prio = 0, prio_mask = 0; 107962306a36Sopenharmony_ci u32 old_index = tab->index; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* Check if vlan is wanted */ 108262306a36Sopenharmony_ci if ((rule->flow_type & FLOW_EXT) && 108362306a36Sopenharmony_ci (rule->m_ext.vlan_tci != cpu_to_be16(0xFFFF))) { 108462306a36Sopenharmony_ci if (!rule->m_ext.vlan_tci) 108562306a36Sopenharmony_ci rule->m_ext.vlan_tci = cpu_to_be16(0xFFFF); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci vlan = RQFPR_VLN; 108862306a36Sopenharmony_ci vlan_mask = RQFPR_VLN; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* Separate the fields */ 109162306a36Sopenharmony_ci id = vlan_tci_vid(rule); 109262306a36Sopenharmony_ci id_mask = vlan_tci_vidm(rule); 109362306a36Sopenharmony_ci cfi = vlan_tci_cfi(rule); 109462306a36Sopenharmony_ci cfi_mask = vlan_tci_cfim(rule); 109562306a36Sopenharmony_ci prio = vlan_tci_prio(rule); 109662306a36Sopenharmony_ci prio_mask = vlan_tci_priom(rule); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (cfi_mask) { 109962306a36Sopenharmony_ci if (cfi) 110062306a36Sopenharmony_ci vlan |= RQFPR_CFI; 110162306a36Sopenharmony_ci vlan_mask |= RQFPR_CFI; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci switch (rule->flow_type & ~FLOW_EXT) { 110662306a36Sopenharmony_ci case TCP_V4_FLOW: 110762306a36Sopenharmony_ci gfar_set_parse_bits(RQFPR_IPV4 | RQFPR_TCP | vlan, 110862306a36Sopenharmony_ci RQFPR_IPV4 | RQFPR_TCP | vlan_mask, tab); 110962306a36Sopenharmony_ci gfar_set_basic_ip(&rule->h_u.tcp_ip4_spec, 111062306a36Sopenharmony_ci &rule->m_u.tcp_ip4_spec, tab); 111162306a36Sopenharmony_ci break; 111262306a36Sopenharmony_ci case UDP_V4_FLOW: 111362306a36Sopenharmony_ci gfar_set_parse_bits(RQFPR_IPV4 | RQFPR_UDP | vlan, 111462306a36Sopenharmony_ci RQFPR_IPV4 | RQFPR_UDP | vlan_mask, tab); 111562306a36Sopenharmony_ci gfar_set_basic_ip(&rule->h_u.udp_ip4_spec, 111662306a36Sopenharmony_ci &rule->m_u.udp_ip4_spec, tab); 111762306a36Sopenharmony_ci break; 111862306a36Sopenharmony_ci case SCTP_V4_FLOW: 111962306a36Sopenharmony_ci gfar_set_parse_bits(RQFPR_IPV4 | vlan, RQFPR_IPV4 | vlan_mask, 112062306a36Sopenharmony_ci tab); 112162306a36Sopenharmony_ci gfar_set_attribute(132, 0, RQFCR_PID_L4P, tab); 112262306a36Sopenharmony_ci gfar_set_basic_ip((struct ethtool_tcpip4_spec *)&rule->h_u, 112362306a36Sopenharmony_ci (struct ethtool_tcpip4_spec *)&rule->m_u, 112462306a36Sopenharmony_ci tab); 112562306a36Sopenharmony_ci break; 112662306a36Sopenharmony_ci case IP_USER_FLOW: 112762306a36Sopenharmony_ci gfar_set_parse_bits(RQFPR_IPV4 | vlan, RQFPR_IPV4 | vlan_mask, 112862306a36Sopenharmony_ci tab); 112962306a36Sopenharmony_ci gfar_set_user_ip((struct ethtool_usrip4_spec *) &rule->h_u, 113062306a36Sopenharmony_ci (struct ethtool_usrip4_spec *) &rule->m_u, 113162306a36Sopenharmony_ci tab); 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci case ETHER_FLOW: 113462306a36Sopenharmony_ci if (vlan) 113562306a36Sopenharmony_ci gfar_set_parse_bits(vlan, vlan_mask, tab); 113662306a36Sopenharmony_ci gfar_set_ether((struct ethhdr *) &rule->h_u, 113762306a36Sopenharmony_ci (struct ethhdr *) &rule->m_u, tab); 113862306a36Sopenharmony_ci break; 113962306a36Sopenharmony_ci default: 114062306a36Sopenharmony_ci return -1; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* Set the vlan attributes in the end */ 114462306a36Sopenharmony_ci if (vlan) { 114562306a36Sopenharmony_ci gfar_set_attribute(id, id_mask, RQFCR_PID_VID, tab); 114662306a36Sopenharmony_ci gfar_set_attribute(prio, prio_mask, RQFCR_PID_PRI, tab); 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* If there has been nothing written till now, it must be a default */ 115062306a36Sopenharmony_ci if (tab->index == old_index) { 115162306a36Sopenharmony_ci gfar_set_mask(0xFFFFFFFF, tab); 115262306a36Sopenharmony_ci tab->fe[tab->index].ctrl = 0x20; 115362306a36Sopenharmony_ci tab->fe[tab->index].prop = 0x0; 115462306a36Sopenharmony_ci tab->index++; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* Remove last AND */ 115862306a36Sopenharmony_ci tab->fe[tab->index - 1].ctrl &= (~RQFCR_AND); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci /* Specify which queue to use or to drop */ 116162306a36Sopenharmony_ci if (rule->ring_cookie == RX_CLS_FLOW_DISC) 116262306a36Sopenharmony_ci tab->fe[tab->index - 1].ctrl |= RQFCR_RJE; 116362306a36Sopenharmony_ci else 116462306a36Sopenharmony_ci tab->fe[tab->index - 1].ctrl |= (rule->ring_cookie << 10); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* Only big enough entries can be clustered */ 116762306a36Sopenharmony_ci if (tab->index > (old_index + 2)) { 116862306a36Sopenharmony_ci tab->fe[old_index + 1].ctrl |= RQFCR_CLE; 116962306a36Sopenharmony_ci tab->fe[tab->index - 1].ctrl |= RQFCR_CLE; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* In rare cases the cache can be full while there is 117362306a36Sopenharmony_ci * free space in hw 117462306a36Sopenharmony_ci */ 117562306a36Sopenharmony_ci if (tab->index > MAX_FILER_CACHE_IDX - 1) 117662306a36Sopenharmony_ci return -EBUSY; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci return 0; 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci/* Write the bit-pattern from software's buffer to hardware registers */ 118262306a36Sopenharmony_cistatic int gfar_write_filer_table(struct gfar_private *priv, 118362306a36Sopenharmony_ci struct filer_table *tab) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci u32 i = 0; 118662306a36Sopenharmony_ci if (tab->index > MAX_FILER_IDX - 1) 118762306a36Sopenharmony_ci return -EBUSY; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* Fill regular entries */ 119062306a36Sopenharmony_ci for (; i < MAX_FILER_IDX && (tab->fe[i].ctrl | tab->fe[i].prop); i++) 119162306a36Sopenharmony_ci gfar_write_filer(priv, i, tab->fe[i].ctrl, tab->fe[i].prop); 119262306a36Sopenharmony_ci /* Fill the rest with fall-troughs */ 119362306a36Sopenharmony_ci for (; i < MAX_FILER_IDX; i++) 119462306a36Sopenharmony_ci gfar_write_filer(priv, i, 0x60, 0xFFFFFFFF); 119562306a36Sopenharmony_ci /* Last entry must be default accept 119662306a36Sopenharmony_ci * because that's what people expect 119762306a36Sopenharmony_ci */ 119862306a36Sopenharmony_ci gfar_write_filer(priv, i, 0x20, 0x0); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci return 0; 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_cistatic int gfar_check_capability(struct ethtool_rx_flow_spec *flow, 120462306a36Sopenharmony_ci struct gfar_private *priv) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (flow->flow_type & FLOW_EXT) { 120862306a36Sopenharmony_ci if (~flow->m_ext.data[0] || ~flow->m_ext.data[1]) 120962306a36Sopenharmony_ci netdev_warn(priv->ndev, 121062306a36Sopenharmony_ci "User-specific data not supported!\n"); 121162306a36Sopenharmony_ci if (~flow->m_ext.vlan_etype) 121262306a36Sopenharmony_ci netdev_warn(priv->ndev, 121362306a36Sopenharmony_ci "VLAN-etype not supported!\n"); 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci if (flow->flow_type == IP_USER_FLOW) 121662306a36Sopenharmony_ci if (flow->h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4) 121762306a36Sopenharmony_ci netdev_warn(priv->ndev, 121862306a36Sopenharmony_ci "IP-Version differing from IPv4 not supported!\n"); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci return 0; 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic int gfar_process_filer_changes(struct gfar_private *priv) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci struct ethtool_flow_spec_container *j; 122662306a36Sopenharmony_ci struct filer_table *tab; 122762306a36Sopenharmony_ci s32 ret = 0; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* So index is set to zero, too! */ 123062306a36Sopenharmony_ci tab = kzalloc(sizeof(*tab), GFP_KERNEL); 123162306a36Sopenharmony_ci if (tab == NULL) 123262306a36Sopenharmony_ci return -ENOMEM; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci /* Now convert the existing filer data from flow_spec into 123562306a36Sopenharmony_ci * filer tables binary format 123662306a36Sopenharmony_ci */ 123762306a36Sopenharmony_ci list_for_each_entry(j, &priv->rx_list.list, list) { 123862306a36Sopenharmony_ci ret = gfar_convert_to_filer(&j->fs, tab); 123962306a36Sopenharmony_ci if (ret == -EBUSY) { 124062306a36Sopenharmony_ci netdev_err(priv->ndev, 124162306a36Sopenharmony_ci "Rule not added: No free space!\n"); 124262306a36Sopenharmony_ci goto end; 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci if (ret == -1) { 124562306a36Sopenharmony_ci netdev_err(priv->ndev, 124662306a36Sopenharmony_ci "Rule not added: Unsupported Flow-type!\n"); 124762306a36Sopenharmony_ci goto end; 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* Write everything to hardware */ 125262306a36Sopenharmony_ci ret = gfar_write_filer_table(priv, tab); 125362306a36Sopenharmony_ci if (ret == -EBUSY) { 125462306a36Sopenharmony_ci netdev_err(priv->ndev, "Rule not added: No free space!\n"); 125562306a36Sopenharmony_ci goto end; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ciend: 125962306a36Sopenharmony_ci kfree(tab); 126062306a36Sopenharmony_ci return ret; 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_cistatic void gfar_invert_masks(struct ethtool_rx_flow_spec *flow) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci u32 i = 0; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci for (i = 0; i < sizeof(flow->m_u); i++) 126862306a36Sopenharmony_ci flow->m_u.hdata[i] ^= 0xFF; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci flow->m_ext.vlan_etype ^= cpu_to_be16(0xFFFF); 127162306a36Sopenharmony_ci flow->m_ext.vlan_tci ^= cpu_to_be16(0xFFFF); 127262306a36Sopenharmony_ci flow->m_ext.data[0] ^= cpu_to_be32(~0); 127362306a36Sopenharmony_ci flow->m_ext.data[1] ^= cpu_to_be32(~0); 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic int gfar_add_cls(struct gfar_private *priv, 127762306a36Sopenharmony_ci struct ethtool_rx_flow_spec *flow) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci struct ethtool_flow_spec_container *temp, *comp; 128062306a36Sopenharmony_ci int ret = 0; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci temp = kmalloc(sizeof(*temp), GFP_KERNEL); 128362306a36Sopenharmony_ci if (temp == NULL) 128462306a36Sopenharmony_ci return -ENOMEM; 128562306a36Sopenharmony_ci memcpy(&temp->fs, flow, sizeof(temp->fs)); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci gfar_invert_masks(&temp->fs); 128862306a36Sopenharmony_ci ret = gfar_check_capability(&temp->fs, priv); 128962306a36Sopenharmony_ci if (ret) 129062306a36Sopenharmony_ci goto clean_mem; 129162306a36Sopenharmony_ci /* Link in the new element at the right @location */ 129262306a36Sopenharmony_ci if (list_empty(&priv->rx_list.list)) { 129362306a36Sopenharmony_ci ret = gfar_check_filer_hardware(priv); 129462306a36Sopenharmony_ci if (ret != 0) 129562306a36Sopenharmony_ci goto clean_mem; 129662306a36Sopenharmony_ci list_add(&temp->list, &priv->rx_list.list); 129762306a36Sopenharmony_ci goto process; 129862306a36Sopenharmony_ci } else { 129962306a36Sopenharmony_ci list_for_each_entry(comp, &priv->rx_list.list, list) { 130062306a36Sopenharmony_ci if (comp->fs.location > flow->location) { 130162306a36Sopenharmony_ci list_add_tail(&temp->list, &comp->list); 130262306a36Sopenharmony_ci goto process; 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci if (comp->fs.location == flow->location) { 130562306a36Sopenharmony_ci netdev_err(priv->ndev, 130662306a36Sopenharmony_ci "Rule not added: ID %d not free!\n", 130762306a36Sopenharmony_ci flow->location); 130862306a36Sopenharmony_ci ret = -EBUSY; 130962306a36Sopenharmony_ci goto clean_mem; 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci list_add_tail(&temp->list, &priv->rx_list.list); 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ciprocess: 131662306a36Sopenharmony_ci priv->rx_list.count++; 131762306a36Sopenharmony_ci ret = gfar_process_filer_changes(priv); 131862306a36Sopenharmony_ci if (ret) 131962306a36Sopenharmony_ci goto clean_list; 132062306a36Sopenharmony_ci return ret; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ciclean_list: 132362306a36Sopenharmony_ci priv->rx_list.count--; 132462306a36Sopenharmony_ci list_del(&temp->list); 132562306a36Sopenharmony_ciclean_mem: 132662306a36Sopenharmony_ci kfree(temp); 132762306a36Sopenharmony_ci return ret; 132862306a36Sopenharmony_ci} 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_cistatic int gfar_del_cls(struct gfar_private *priv, u32 loc) 133162306a36Sopenharmony_ci{ 133262306a36Sopenharmony_ci struct ethtool_flow_spec_container *comp; 133362306a36Sopenharmony_ci u32 ret = -EINVAL; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci if (list_empty(&priv->rx_list.list)) 133662306a36Sopenharmony_ci return ret; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci list_for_each_entry(comp, &priv->rx_list.list, list) { 133962306a36Sopenharmony_ci if (comp->fs.location == loc) { 134062306a36Sopenharmony_ci list_del(&comp->list); 134162306a36Sopenharmony_ci kfree(comp); 134262306a36Sopenharmony_ci priv->rx_list.count--; 134362306a36Sopenharmony_ci gfar_process_filer_changes(priv); 134462306a36Sopenharmony_ci ret = 0; 134562306a36Sopenharmony_ci break; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci return ret; 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic int gfar_get_cls(struct gfar_private *priv, struct ethtool_rxnfc *cmd) 135362306a36Sopenharmony_ci{ 135462306a36Sopenharmony_ci struct ethtool_flow_spec_container *comp; 135562306a36Sopenharmony_ci u32 ret = -EINVAL; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci list_for_each_entry(comp, &priv->rx_list.list, list) { 135862306a36Sopenharmony_ci if (comp->fs.location == cmd->fs.location) { 135962306a36Sopenharmony_ci memcpy(&cmd->fs, &comp->fs, sizeof(cmd->fs)); 136062306a36Sopenharmony_ci gfar_invert_masks(&cmd->fs); 136162306a36Sopenharmony_ci ret = 0; 136262306a36Sopenharmony_ci break; 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci return ret; 136762306a36Sopenharmony_ci} 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_cistatic int gfar_get_cls_all(struct gfar_private *priv, 137062306a36Sopenharmony_ci struct ethtool_rxnfc *cmd, u32 *rule_locs) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct ethtool_flow_spec_container *comp; 137362306a36Sopenharmony_ci u32 i = 0; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci list_for_each_entry(comp, &priv->rx_list.list, list) { 137662306a36Sopenharmony_ci if (i == cmd->rule_cnt) 137762306a36Sopenharmony_ci return -EMSGSIZE; 137862306a36Sopenharmony_ci rule_locs[i] = comp->fs.location; 137962306a36Sopenharmony_ci i++; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci cmd->data = MAX_FILER_IDX; 138362306a36Sopenharmony_ci cmd->rule_cnt = i; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci return 0; 138662306a36Sopenharmony_ci} 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_cistatic int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd) 138962306a36Sopenharmony_ci{ 139062306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 139162306a36Sopenharmony_ci int ret = 0; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (test_bit(GFAR_RESETTING, &priv->state)) 139462306a36Sopenharmony_ci return -EBUSY; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci mutex_lock(&priv->rx_queue_access); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci switch (cmd->cmd) { 139962306a36Sopenharmony_ci case ETHTOOL_SRXFH: 140062306a36Sopenharmony_ci ret = gfar_set_hash_opts(priv, cmd); 140162306a36Sopenharmony_ci break; 140262306a36Sopenharmony_ci case ETHTOOL_SRXCLSRLINS: 140362306a36Sopenharmony_ci if ((cmd->fs.ring_cookie != RX_CLS_FLOW_DISC && 140462306a36Sopenharmony_ci cmd->fs.ring_cookie >= priv->num_rx_queues) || 140562306a36Sopenharmony_ci cmd->fs.location >= MAX_FILER_IDX) { 140662306a36Sopenharmony_ci ret = -EINVAL; 140762306a36Sopenharmony_ci break; 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci ret = gfar_add_cls(priv, &cmd->fs); 141062306a36Sopenharmony_ci break; 141162306a36Sopenharmony_ci case ETHTOOL_SRXCLSRLDEL: 141262306a36Sopenharmony_ci ret = gfar_del_cls(priv, cmd->fs.location); 141362306a36Sopenharmony_ci break; 141462306a36Sopenharmony_ci default: 141562306a36Sopenharmony_ci ret = -EINVAL; 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci mutex_unlock(&priv->rx_queue_access); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci return ret; 142162306a36Sopenharmony_ci} 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_cistatic int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 142462306a36Sopenharmony_ci u32 *rule_locs) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 142762306a36Sopenharmony_ci int ret = 0; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci switch (cmd->cmd) { 143062306a36Sopenharmony_ci case ETHTOOL_GRXRINGS: 143162306a36Sopenharmony_ci cmd->data = priv->num_rx_queues; 143262306a36Sopenharmony_ci break; 143362306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLCNT: 143462306a36Sopenharmony_ci cmd->rule_cnt = priv->rx_list.count; 143562306a36Sopenharmony_ci break; 143662306a36Sopenharmony_ci case ETHTOOL_GRXCLSRULE: 143762306a36Sopenharmony_ci ret = gfar_get_cls(priv, cmd); 143862306a36Sopenharmony_ci break; 143962306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLALL: 144062306a36Sopenharmony_ci ret = gfar_get_cls_all(priv, cmd, rule_locs); 144162306a36Sopenharmony_ci break; 144262306a36Sopenharmony_ci default: 144362306a36Sopenharmony_ci ret = -EINVAL; 144462306a36Sopenharmony_ci break; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci return ret; 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic int gfar_get_ts_info(struct net_device *dev, 145162306a36Sopenharmony_ci struct ethtool_ts_info *info) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 145462306a36Sopenharmony_ci struct platform_device *ptp_dev; 145562306a36Sopenharmony_ci struct device_node *ptp_node; 145662306a36Sopenharmony_ci struct ptp_qoriq *ptp = NULL; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci info->phc_index = -1; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)) { 146162306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | 146262306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_SOFTWARE | 146362306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE; 146462306a36Sopenharmony_ci return 0; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci ptp_node = of_find_compatible_node(NULL, NULL, "fsl,etsec-ptp"); 146862306a36Sopenharmony_ci if (ptp_node) { 146962306a36Sopenharmony_ci ptp_dev = of_find_device_by_node(ptp_node); 147062306a36Sopenharmony_ci of_node_put(ptp_node); 147162306a36Sopenharmony_ci if (ptp_dev) 147262306a36Sopenharmony_ci ptp = platform_get_drvdata(ptp_dev); 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci if (ptp) 147662306a36Sopenharmony_ci info->phc_index = ptp->phc_index; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | 147962306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 148062306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE | 148162306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 148262306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_SOFTWARE | 148362306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE; 148462306a36Sopenharmony_ci info->tx_types = (1 << HWTSTAMP_TX_OFF) | 148562306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ON); 148662306a36Sopenharmony_ci info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 148762306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_ALL); 148862306a36Sopenharmony_ci return 0; 148962306a36Sopenharmony_ci} 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ciconst struct ethtool_ops gfar_ethtool_ops = { 149262306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 149362306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 149462306a36Sopenharmony_ci .get_drvinfo = gfar_gdrvinfo, 149562306a36Sopenharmony_ci .get_regs_len = gfar_reglen, 149662306a36Sopenharmony_ci .get_regs = gfar_get_regs, 149762306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 149862306a36Sopenharmony_ci .get_coalesce = gfar_gcoalesce, 149962306a36Sopenharmony_ci .set_coalesce = gfar_scoalesce, 150062306a36Sopenharmony_ci .get_ringparam = gfar_gringparam, 150162306a36Sopenharmony_ci .set_ringparam = gfar_sringparam, 150262306a36Sopenharmony_ci .get_pauseparam = gfar_gpauseparam, 150362306a36Sopenharmony_ci .set_pauseparam = gfar_spauseparam, 150462306a36Sopenharmony_ci .get_strings = gfar_gstrings, 150562306a36Sopenharmony_ci .get_sset_count = gfar_sset_count, 150662306a36Sopenharmony_ci .get_ethtool_stats = gfar_fill_stats, 150762306a36Sopenharmony_ci .get_msglevel = gfar_get_msglevel, 150862306a36Sopenharmony_ci .set_msglevel = gfar_set_msglevel, 150962306a36Sopenharmony_ci#ifdef CONFIG_PM 151062306a36Sopenharmony_ci .get_wol = gfar_get_wol, 151162306a36Sopenharmony_ci .set_wol = gfar_set_wol, 151262306a36Sopenharmony_ci#endif 151362306a36Sopenharmony_ci .set_rxnfc = gfar_set_nfc, 151462306a36Sopenharmony_ci .get_rxnfc = gfar_get_nfc, 151562306a36Sopenharmony_ci .get_ts_info = gfar_get_ts_info, 151662306a36Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 151762306a36Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 151862306a36Sopenharmony_ci}; 1519