18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/ethernet/freescale/gianfar_ethtool.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Gianfar Ethernet Driver 68c2ecf20Sopenharmony_ci * Ethtool support for Gianfar Enet 78c2ecf20Sopenharmony_ci * Based on e1000 ethtool support 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author: Andy Fleming 108c2ecf20Sopenharmony_ci * Maintainer: Kumar Gala 118c2ecf20Sopenharmony_ci * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Copyright 2003-2006, 2008-2009, 2011 Freescale Semiconductor, Inc. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/string.h> 208c2ecf20Sopenharmony_ci#include <linux/errno.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 248c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h> 268c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 278c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 288c2ecf20Sopenharmony_ci#include <linux/mm.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <asm/io.h> 318c2ecf20Sopenharmony_ci#include <asm/irq.h> 328c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 338c2ecf20Sopenharmony_ci#include <linux/module.h> 348c2ecf20Sopenharmony_ci#include <linux/crc32.h> 358c2ecf20Sopenharmony_ci#include <asm/types.h> 368c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 378c2ecf20Sopenharmony_ci#include <linux/mii.h> 388c2ecf20Sopenharmony_ci#include <linux/phy.h> 398c2ecf20Sopenharmony_ci#include <linux/sort.h> 408c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 418c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 428c2ecf20Sopenharmony_ci#include <linux/fsl/ptp_qoriq.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include "gianfar.h" 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define GFAR_MAX_COAL_USECS 0xffff 478c2ecf20Sopenharmony_ci#define GFAR_MAX_COAL_FRAMES 0xff 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic const char stat_gstrings[][ETH_GSTRING_LEN] = { 508c2ecf20Sopenharmony_ci /* extra stats */ 518c2ecf20Sopenharmony_ci "rx-allocation-errors", 528c2ecf20Sopenharmony_ci "rx-large-frame-errors", 538c2ecf20Sopenharmony_ci "rx-short-frame-errors", 548c2ecf20Sopenharmony_ci "rx-non-octet-errors", 558c2ecf20Sopenharmony_ci "rx-crc-errors", 568c2ecf20Sopenharmony_ci "rx-overrun-errors", 578c2ecf20Sopenharmony_ci "rx-busy-errors", 588c2ecf20Sopenharmony_ci "rx-babbling-errors", 598c2ecf20Sopenharmony_ci "rx-truncated-frames", 608c2ecf20Sopenharmony_ci "ethernet-bus-error", 618c2ecf20Sopenharmony_ci "tx-babbling-errors", 628c2ecf20Sopenharmony_ci "tx-underrun-errors", 638c2ecf20Sopenharmony_ci "tx-timeout-errors", 648c2ecf20Sopenharmony_ci /* rmon stats */ 658c2ecf20Sopenharmony_ci "tx-rx-64-frames", 668c2ecf20Sopenharmony_ci "tx-rx-65-127-frames", 678c2ecf20Sopenharmony_ci "tx-rx-128-255-frames", 688c2ecf20Sopenharmony_ci "tx-rx-256-511-frames", 698c2ecf20Sopenharmony_ci "tx-rx-512-1023-frames", 708c2ecf20Sopenharmony_ci "tx-rx-1024-1518-frames", 718c2ecf20Sopenharmony_ci "tx-rx-1519-1522-good-vlan", 728c2ecf20Sopenharmony_ci "rx-bytes", 738c2ecf20Sopenharmony_ci "rx-packets", 748c2ecf20Sopenharmony_ci "rx-fcs-errors", 758c2ecf20Sopenharmony_ci "receive-multicast-packet", 768c2ecf20Sopenharmony_ci "receive-broadcast-packet", 778c2ecf20Sopenharmony_ci "rx-control-frame-packets", 788c2ecf20Sopenharmony_ci "rx-pause-frame-packets", 798c2ecf20Sopenharmony_ci "rx-unknown-op-code", 808c2ecf20Sopenharmony_ci "rx-alignment-error", 818c2ecf20Sopenharmony_ci "rx-frame-length-error", 828c2ecf20Sopenharmony_ci "rx-code-error", 838c2ecf20Sopenharmony_ci "rx-carrier-sense-error", 848c2ecf20Sopenharmony_ci "rx-undersize-packets", 858c2ecf20Sopenharmony_ci "rx-oversize-packets", 868c2ecf20Sopenharmony_ci "rx-fragmented-frames", 878c2ecf20Sopenharmony_ci "rx-jabber-frames", 888c2ecf20Sopenharmony_ci "rx-dropped-frames", 898c2ecf20Sopenharmony_ci "tx-byte-counter", 908c2ecf20Sopenharmony_ci "tx-packets", 918c2ecf20Sopenharmony_ci "tx-multicast-packets", 928c2ecf20Sopenharmony_ci "tx-broadcast-packets", 938c2ecf20Sopenharmony_ci "tx-pause-control-frames", 948c2ecf20Sopenharmony_ci "tx-deferral-packets", 958c2ecf20Sopenharmony_ci "tx-excessive-deferral-packets", 968c2ecf20Sopenharmony_ci "tx-single-collision-packets", 978c2ecf20Sopenharmony_ci "tx-multiple-collision-packets", 988c2ecf20Sopenharmony_ci "tx-late-collision-packets", 998c2ecf20Sopenharmony_ci "tx-excessive-collision-packets", 1008c2ecf20Sopenharmony_ci "tx-total-collision", 1018c2ecf20Sopenharmony_ci "reserved", 1028c2ecf20Sopenharmony_ci "tx-dropped-frames", 1038c2ecf20Sopenharmony_ci "tx-jabber-frames", 1048c2ecf20Sopenharmony_ci "tx-fcs-errors", 1058c2ecf20Sopenharmony_ci "tx-control-frames", 1068c2ecf20Sopenharmony_ci "tx-oversize-frames", 1078c2ecf20Sopenharmony_ci "tx-undersize-frames", 1088c2ecf20Sopenharmony_ci "tx-fragmented-frames", 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* Fill in a buffer with the strings which correspond to the 1128c2ecf20Sopenharmony_ci * stats */ 1138c2ecf20Sopenharmony_cistatic void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) 1188c2ecf20Sopenharmony_ci memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN); 1198c2ecf20Sopenharmony_ci else 1208c2ecf20Sopenharmony_ci memcpy(buf, stat_gstrings, 1218c2ecf20Sopenharmony_ci GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* Fill in an array of 64-bit statistics from various sources. 1258c2ecf20Sopenharmony_ci * This array will be appended to the end of the ethtool_stats 1268c2ecf20Sopenharmony_ci * structure, and returned to user space 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_cistatic void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, 1298c2ecf20Sopenharmony_ci u64 *buf) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci int i; 1328c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 1338c2ecf20Sopenharmony_ci struct gfar __iomem *regs = priv->gfargrp[0].regs; 1348c2ecf20Sopenharmony_ci atomic64_t *extra = (atomic64_t *)&priv->extra_stats; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) 1378c2ecf20Sopenharmony_ci buf[i] = atomic64_read(&extra[i]); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) { 1408c2ecf20Sopenharmony_ci u32 __iomem *rmon = (u32 __iomem *) ®s->rmon; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci for (; i < GFAR_STATS_LEN; i++, rmon++) 1438c2ecf20Sopenharmony_ci buf[i] = (u64) gfar_read(rmon); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int gfar_sset_count(struct net_device *dev, int sset) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci switch (sset) { 1528c2ecf20Sopenharmony_ci case ETH_SS_STATS: 1538c2ecf20Sopenharmony_ci if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) 1548c2ecf20Sopenharmony_ci return GFAR_STATS_LEN; 1558c2ecf20Sopenharmony_ci else 1568c2ecf20Sopenharmony_ci return GFAR_EXTRA_STATS_LEN; 1578c2ecf20Sopenharmony_ci default: 1588c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* Fills in the drvinfo structure with some basic info */ 1638c2ecf20Sopenharmony_cistatic void gfar_gdrvinfo(struct net_device *dev, 1648c2ecf20Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* Return the length of the register structure */ 1708c2ecf20Sopenharmony_cistatic int gfar_reglen(struct net_device *dev) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci return sizeof (struct gfar); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* Return a dump of the GFAR register space */ 1768c2ecf20Sopenharmony_cistatic void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, 1778c2ecf20Sopenharmony_ci void *regbuf) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int i; 1808c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 1818c2ecf20Sopenharmony_ci u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp[0].regs; 1828c2ecf20Sopenharmony_ci u32 *buf = (u32 *) regbuf; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++) 1858c2ecf20Sopenharmony_ci buf[i] = gfar_read(&theregs[i]); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* Convert microseconds to ethernet clock ticks, which changes 1898c2ecf20Sopenharmony_ci * depending on what speed the controller is running at */ 1908c2ecf20Sopenharmony_cistatic unsigned int gfar_usecs2ticks(struct gfar_private *priv, 1918c2ecf20Sopenharmony_ci unsigned int usecs) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct net_device *ndev = priv->ndev; 1948c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 1958c2ecf20Sopenharmony_ci unsigned int count; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* The timer is different, depending on the interface speed */ 1988c2ecf20Sopenharmony_ci switch (phydev->speed) { 1998c2ecf20Sopenharmony_ci case SPEED_1000: 2008c2ecf20Sopenharmony_ci count = GFAR_GBIT_TIME; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case SPEED_100: 2038c2ecf20Sopenharmony_ci count = GFAR_100_TIME; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci case SPEED_10: 2068c2ecf20Sopenharmony_ci default: 2078c2ecf20Sopenharmony_ci count = GFAR_10_TIME; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Make sure we return a number greater than 0 2128c2ecf20Sopenharmony_ci * if usecs > 0 */ 2138c2ecf20Sopenharmony_ci return DIV_ROUND_UP(usecs * 1000, count); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* Convert ethernet clock ticks to microseconds */ 2178c2ecf20Sopenharmony_cistatic unsigned int gfar_ticks2usecs(struct gfar_private *priv, 2188c2ecf20Sopenharmony_ci unsigned int ticks) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct net_device *ndev = priv->ndev; 2218c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 2228c2ecf20Sopenharmony_ci unsigned int count; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* The timer is different, depending on the interface speed */ 2258c2ecf20Sopenharmony_ci switch (phydev->speed) { 2268c2ecf20Sopenharmony_ci case SPEED_1000: 2278c2ecf20Sopenharmony_ci count = GFAR_GBIT_TIME; 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci case SPEED_100: 2308c2ecf20Sopenharmony_ci count = GFAR_100_TIME; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci case SPEED_10: 2338c2ecf20Sopenharmony_ci default: 2348c2ecf20Sopenharmony_ci count = GFAR_10_TIME; 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Make sure we return a number greater than 0 */ 2398c2ecf20Sopenharmony_ci /* if ticks is > 0 */ 2408c2ecf20Sopenharmony_ci return (ticks * count) / 1000; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* Get the coalescing parameters, and put them in the cvals 2448c2ecf20Sopenharmony_ci * structure. */ 2458c2ecf20Sopenharmony_cistatic int gfar_gcoalesce(struct net_device *dev, 2468c2ecf20Sopenharmony_ci struct ethtool_coalesce *cvals) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 2498c2ecf20Sopenharmony_ci struct gfar_priv_rx_q *rx_queue = NULL; 2508c2ecf20Sopenharmony_ci struct gfar_priv_tx_q *tx_queue = NULL; 2518c2ecf20Sopenharmony_ci unsigned long rxtime; 2528c2ecf20Sopenharmony_ci unsigned long rxcount; 2538c2ecf20Sopenharmony_ci unsigned long txtime; 2548c2ecf20Sopenharmony_ci unsigned long txcount; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) 2578c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!dev->phydev) 2608c2ecf20Sopenharmony_ci return -ENODEV; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci rx_queue = priv->rx_queue[0]; 2638c2ecf20Sopenharmony_ci tx_queue = priv->tx_queue[0]; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci rxtime = get_ictt_value(rx_queue->rxic); 2668c2ecf20Sopenharmony_ci rxcount = get_icft_value(rx_queue->rxic); 2678c2ecf20Sopenharmony_ci txtime = get_ictt_value(tx_queue->txic); 2688c2ecf20Sopenharmony_ci txcount = get_icft_value(tx_queue->txic); 2698c2ecf20Sopenharmony_ci cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime); 2708c2ecf20Sopenharmony_ci cvals->rx_max_coalesced_frames = rxcount; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, txtime); 2738c2ecf20Sopenharmony_ci cvals->tx_max_coalesced_frames = txcount; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* Change the coalescing values. 2798c2ecf20Sopenharmony_ci * Both cvals->*_usecs and cvals->*_frames have to be > 0 2808c2ecf20Sopenharmony_ci * in order for coalescing to be active 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_cistatic int gfar_scoalesce(struct net_device *dev, 2838c2ecf20Sopenharmony_ci struct ethtool_coalesce *cvals) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 2868c2ecf20Sopenharmony_ci int i, err = 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) 2898c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (!dev->phydev) 2928c2ecf20Sopenharmony_ci return -ENODEV; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Check the bounds of the values */ 2958c2ecf20Sopenharmony_ci if (cvals->rx_coalesce_usecs > GFAR_MAX_COAL_USECS) { 2968c2ecf20Sopenharmony_ci netdev_info(dev, "Coalescing is limited to %d microseconds\n", 2978c2ecf20Sopenharmony_ci GFAR_MAX_COAL_USECS); 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (cvals->rx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) { 3028c2ecf20Sopenharmony_ci netdev_info(dev, "Coalescing is limited to %d frames\n", 3038c2ecf20Sopenharmony_ci GFAR_MAX_COAL_FRAMES); 3048c2ecf20Sopenharmony_ci return -EINVAL; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* Check the bounds of the values */ 3088c2ecf20Sopenharmony_ci if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) { 3098c2ecf20Sopenharmony_ci netdev_info(dev, "Coalescing is limited to %d microseconds\n", 3108c2ecf20Sopenharmony_ci GFAR_MAX_COAL_USECS); 3118c2ecf20Sopenharmony_ci return -EINVAL; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) { 3158c2ecf20Sopenharmony_ci netdev_info(dev, "Coalescing is limited to %d frames\n", 3168c2ecf20Sopenharmony_ci GFAR_MAX_COAL_FRAMES); 3178c2ecf20Sopenharmony_ci return -EINVAL; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) 3218c2ecf20Sopenharmony_ci cpu_relax(); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Set up rx coalescing */ 3248c2ecf20Sopenharmony_ci if ((cvals->rx_coalesce_usecs == 0) || 3258c2ecf20Sopenharmony_ci (cvals->rx_max_coalesced_frames == 0)) { 3268c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_rx_queues; i++) 3278c2ecf20Sopenharmony_ci priv->rx_queue[i]->rxcoalescing = 0; 3288c2ecf20Sopenharmony_ci } else { 3298c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_rx_queues; i++) 3308c2ecf20Sopenharmony_ci priv->rx_queue[i]->rxcoalescing = 1; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_rx_queues; i++) { 3348c2ecf20Sopenharmony_ci priv->rx_queue[i]->rxic = mk_ic_value( 3358c2ecf20Sopenharmony_ci cvals->rx_max_coalesced_frames, 3368c2ecf20Sopenharmony_ci gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs)); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Set up tx coalescing */ 3408c2ecf20Sopenharmony_ci if ((cvals->tx_coalesce_usecs == 0) || 3418c2ecf20Sopenharmony_ci (cvals->tx_max_coalesced_frames == 0)) { 3428c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_tx_queues; i++) 3438c2ecf20Sopenharmony_ci priv->tx_queue[i]->txcoalescing = 0; 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_tx_queues; i++) 3468c2ecf20Sopenharmony_ci priv->tx_queue[i]->txcoalescing = 1; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_tx_queues; i++) { 3508c2ecf20Sopenharmony_ci priv->tx_queue[i]->txic = mk_ic_value( 3518c2ecf20Sopenharmony_ci cvals->tx_max_coalesced_frames, 3528c2ecf20Sopenharmony_ci gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs)); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (dev->flags & IFF_UP) { 3568c2ecf20Sopenharmony_ci stop_gfar(dev); 3578c2ecf20Sopenharmony_ci err = startup_gfar(dev); 3588c2ecf20Sopenharmony_ci } else { 3598c2ecf20Sopenharmony_ci gfar_mac_reset(priv); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci clear_bit_unlock(GFAR_RESETTING, &priv->state); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return err; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* Fills in rvals with the current ring parameters. Currently, 3688c2ecf20Sopenharmony_ci * rx, rx_mini, and rx_jumbo rings are the same size, as mini and 3698c2ecf20Sopenharmony_ci * jumbo are ignored by the driver */ 3708c2ecf20Sopenharmony_cistatic void gfar_gringparam(struct net_device *dev, 3718c2ecf20Sopenharmony_ci struct ethtool_ringparam *rvals) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 3748c2ecf20Sopenharmony_ci struct gfar_priv_tx_q *tx_queue = NULL; 3758c2ecf20Sopenharmony_ci struct gfar_priv_rx_q *rx_queue = NULL; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci tx_queue = priv->tx_queue[0]; 3788c2ecf20Sopenharmony_ci rx_queue = priv->rx_queue[0]; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE; 3818c2ecf20Sopenharmony_ci rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE; 3828c2ecf20Sopenharmony_ci rvals->rx_jumbo_max_pending = GFAR_RX_MAX_RING_SIZE; 3838c2ecf20Sopenharmony_ci rvals->tx_max_pending = GFAR_TX_MAX_RING_SIZE; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* Values changeable by the user. The valid values are 3868c2ecf20Sopenharmony_ci * in the range 1 to the "*_max_pending" counterpart above. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci rvals->rx_pending = rx_queue->rx_ring_size; 3898c2ecf20Sopenharmony_ci rvals->rx_mini_pending = rx_queue->rx_ring_size; 3908c2ecf20Sopenharmony_ci rvals->rx_jumbo_pending = rx_queue->rx_ring_size; 3918c2ecf20Sopenharmony_ci rvals->tx_pending = tx_queue->tx_ring_size; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* Change the current ring parameters, stopping the controller if 3958c2ecf20Sopenharmony_ci * necessary so that we don't mess things up while we're in motion. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_cistatic int gfar_sringparam(struct net_device *dev, 3988c2ecf20Sopenharmony_ci struct ethtool_ringparam *rvals) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 4018c2ecf20Sopenharmony_ci int err = 0, i; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE) 4048c2ecf20Sopenharmony_ci return -EINVAL; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (!is_power_of_2(rvals->rx_pending)) { 4078c2ecf20Sopenharmony_ci netdev_err(dev, "Ring sizes must be a power of 2\n"); 4088c2ecf20Sopenharmony_ci return -EINVAL; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (rvals->tx_pending > GFAR_TX_MAX_RING_SIZE) 4128c2ecf20Sopenharmony_ci return -EINVAL; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (!is_power_of_2(rvals->tx_pending)) { 4158c2ecf20Sopenharmony_ci netdev_err(dev, "Ring sizes must be a power of 2\n"); 4168c2ecf20Sopenharmony_ci return -EINVAL; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) 4208c2ecf20Sopenharmony_ci cpu_relax(); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (dev->flags & IFF_UP) 4238c2ecf20Sopenharmony_ci stop_gfar(dev); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* Change the sizes */ 4268c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_rx_queues; i++) 4278c2ecf20Sopenharmony_ci priv->rx_queue[i]->rx_ring_size = rvals->rx_pending; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_tx_queues; i++) 4308c2ecf20Sopenharmony_ci priv->tx_queue[i]->tx_ring_size = rvals->tx_pending; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Rebuild the rings with the new size */ 4338c2ecf20Sopenharmony_ci if (dev->flags & IFF_UP) 4348c2ecf20Sopenharmony_ci err = startup_gfar(dev); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci clear_bit_unlock(GFAR_RESETTING, &priv->state); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return err; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic void gfar_gpauseparam(struct net_device *dev, 4428c2ecf20Sopenharmony_ci struct ethtool_pauseparam *epause) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci epause->autoneg = !!priv->pause_aneg_en; 4478c2ecf20Sopenharmony_ci epause->rx_pause = !!priv->rx_pause_en; 4488c2ecf20Sopenharmony_ci epause->tx_pause = !!priv->tx_pause_en; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic int gfar_spauseparam(struct net_device *dev, 4528c2ecf20Sopenharmony_ci struct ethtool_pauseparam *epause) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 4558c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 4568c2ecf20Sopenharmony_ci struct gfar __iomem *regs = priv->gfargrp[0].regs; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!phydev) 4598c2ecf20Sopenharmony_ci return -ENODEV; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (!phy_validate_pause(phydev, epause)) 4628c2ecf20Sopenharmony_ci return -EINVAL; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci priv->rx_pause_en = priv->tx_pause_en = 0; 4658c2ecf20Sopenharmony_ci phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause); 4668c2ecf20Sopenharmony_ci if (epause->rx_pause) { 4678c2ecf20Sopenharmony_ci priv->rx_pause_en = 1; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (epause->tx_pause) { 4708c2ecf20Sopenharmony_ci priv->tx_pause_en = 1; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } else if (epause->tx_pause) { 4738c2ecf20Sopenharmony_ci priv->tx_pause_en = 1; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (epause->autoneg) 4778c2ecf20Sopenharmony_ci priv->pause_aneg_en = 1; 4788c2ecf20Sopenharmony_ci else 4798c2ecf20Sopenharmony_ci priv->pause_aneg_en = 0; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (!epause->autoneg) { 4828c2ecf20Sopenharmony_ci u32 tempval = gfar_read(®s->maccfg1); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci priv->tx_actual_en = 0; 4878c2ecf20Sopenharmony_ci if (priv->tx_pause_en) { 4888c2ecf20Sopenharmony_ci priv->tx_actual_en = 1; 4898c2ecf20Sopenharmony_ci tempval |= MACCFG1_TX_FLOW; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (priv->rx_pause_en) 4938c2ecf20Sopenharmony_ci tempval |= MACCFG1_RX_FLOW; 4948c2ecf20Sopenharmony_ci gfar_write(®s->maccfg1, tempval); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return 0; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ciint gfar_set_features(struct net_device *dev, netdev_features_t features) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci netdev_features_t changed = dev->features ^ features; 5038c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 5048c2ecf20Sopenharmony_ci int err = 0; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | 5078c2ecf20Sopenharmony_ci NETIF_F_RXCSUM))) 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) 5118c2ecf20Sopenharmony_ci cpu_relax(); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci dev->features = features; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (dev->flags & IFF_UP) { 5168c2ecf20Sopenharmony_ci /* Now we take down the rings to rebuild them */ 5178c2ecf20Sopenharmony_ci stop_gfar(dev); 5188c2ecf20Sopenharmony_ci err = startup_gfar(dev); 5198c2ecf20Sopenharmony_ci } else { 5208c2ecf20Sopenharmony_ci gfar_mac_reset(priv); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci clear_bit_unlock(GFAR_RESETTING, &priv->state); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return err; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic uint32_t gfar_get_msglevel(struct net_device *dev) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return priv->msg_enable; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic void gfar_set_msglevel(struct net_device *dev, uint32_t data) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci priv->msg_enable = data; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5438c2ecf20Sopenharmony_cistatic void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci wol->supported = 0; 5488c2ecf20Sopenharmony_ci wol->wolopts = 0; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (priv->wol_supported & GFAR_WOL_MAGIC) 5518c2ecf20Sopenharmony_ci wol->supported |= WAKE_MAGIC; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (priv->wol_supported & GFAR_WOL_FILER_UCAST) 5548c2ecf20Sopenharmony_ci wol->supported |= WAKE_UCAST; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (priv->wol_opts & GFAR_WOL_MAGIC) 5578c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (priv->wol_opts & GFAR_WOL_FILER_UCAST) 5608c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_UCAST; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 5668c2ecf20Sopenharmony_ci u16 wol_opts = 0; 5678c2ecf20Sopenharmony_ci int err; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!priv->wol_supported && wol->wolopts) 5708c2ecf20Sopenharmony_ci return -EINVAL; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (wol->wolopts & ~(WAKE_MAGIC | WAKE_UCAST)) 5738c2ecf20Sopenharmony_ci return -EINVAL; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) { 5768c2ecf20Sopenharmony_ci wol_opts |= GFAR_WOL_MAGIC; 5778c2ecf20Sopenharmony_ci } else { 5788c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_UCAST) 5798c2ecf20Sopenharmony_ci wol_opts |= GFAR_WOL_FILER_UCAST; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci wol_opts &= priv->wol_supported; 5838c2ecf20Sopenharmony_ci priv->wol_opts = 0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci err = device_set_wakeup_enable(priv->dev, wol_opts); 5868c2ecf20Sopenharmony_ci if (err) 5878c2ecf20Sopenharmony_ci return err; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci priv->wol_opts = wol_opts; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci#endif 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci u32 fcr = 0x0, fpr = FPR_FILER_MASK; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (ethflow & RXH_L2DA) { 6008c2ecf20Sopenharmony_ci fcr = RQFCR_PID_DAH | RQFCR_CMP_NOMATCH | 6018c2ecf20Sopenharmony_ci RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; 6028c2ecf20Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 6038c2ecf20Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 6048c2ecf20Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 6058c2ecf20Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci fcr = RQFCR_PID_DAL | RQFCR_CMP_NOMATCH | 6088c2ecf20Sopenharmony_ci RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; 6098c2ecf20Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 6108c2ecf20Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 6118c2ecf20Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 6128c2ecf20Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (ethflow & RXH_VLAN) { 6168c2ecf20Sopenharmony_ci fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH | 6178c2ecf20Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 6188c2ecf20Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 6198c2ecf20Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 6208c2ecf20Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 6218c2ecf20Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (ethflow & RXH_IP_SRC) { 6258c2ecf20Sopenharmony_ci fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | 6268c2ecf20Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 6278c2ecf20Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 6288c2ecf20Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 6298c2ecf20Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 6308c2ecf20Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (ethflow & (RXH_IP_DST)) { 6348c2ecf20Sopenharmony_ci fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | 6358c2ecf20Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 6368c2ecf20Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 6378c2ecf20Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 6388c2ecf20Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 6398c2ecf20Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (ethflow & RXH_L3_PROTO) { 6438c2ecf20Sopenharmony_ci fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH | 6448c2ecf20Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 6458c2ecf20Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 6468c2ecf20Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 6478c2ecf20Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 6488c2ecf20Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (ethflow & RXH_L4_B_0_1) { 6528c2ecf20Sopenharmony_ci fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | 6538c2ecf20Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 6548c2ecf20Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 6558c2ecf20Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 6568c2ecf20Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 6578c2ecf20Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (ethflow & RXH_L4_B_2_3) { 6618c2ecf20Sopenharmony_ci fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | 6628c2ecf20Sopenharmony_ci RQFCR_AND | RQFCR_HASHTBL_0; 6638c2ecf20Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; 6648c2ecf20Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; 6658c2ecf20Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); 6668c2ecf20Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, 6718c2ecf20Sopenharmony_ci u64 class) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci unsigned int cmp_rqfpr; 6748c2ecf20Sopenharmony_ci unsigned int *local_rqfpr; 6758c2ecf20Sopenharmony_ci unsigned int *local_rqfcr; 6768c2ecf20Sopenharmony_ci int i = 0x0, k = 0x0; 6778c2ecf20Sopenharmony_ci int j = MAX_FILER_IDX, l = 0x0; 6788c2ecf20Sopenharmony_ci int ret = 1; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci local_rqfpr = kmalloc_array(MAX_FILER_IDX + 1, sizeof(unsigned int), 6818c2ecf20Sopenharmony_ci GFP_KERNEL); 6828c2ecf20Sopenharmony_ci local_rqfcr = kmalloc_array(MAX_FILER_IDX + 1, sizeof(unsigned int), 6838c2ecf20Sopenharmony_ci GFP_KERNEL); 6848c2ecf20Sopenharmony_ci if (!local_rqfpr || !local_rqfcr) { 6858c2ecf20Sopenharmony_ci ret = 0; 6868c2ecf20Sopenharmony_ci goto err; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci switch (class) { 6908c2ecf20Sopenharmony_ci case TCP_V4_FLOW: 6918c2ecf20Sopenharmony_ci cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP; 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci case UDP_V4_FLOW: 6948c2ecf20Sopenharmony_ci cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP; 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci case TCP_V6_FLOW: 6978c2ecf20Sopenharmony_ci cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP; 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci case UDP_V6_FLOW: 7008c2ecf20Sopenharmony_ci cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP; 7018c2ecf20Sopenharmony_ci break; 7028c2ecf20Sopenharmony_ci default: 7038c2ecf20Sopenharmony_ci netdev_err(priv->ndev, 7048c2ecf20Sopenharmony_ci "Right now this class is not supported\n"); 7058c2ecf20Sopenharmony_ci ret = 0; 7068c2ecf20Sopenharmony_ci goto err; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci for (i = 0; i < MAX_FILER_IDX + 1; i++) { 7108c2ecf20Sopenharmony_ci local_rqfpr[j] = priv->ftp_rqfpr[i]; 7118c2ecf20Sopenharmony_ci local_rqfcr[j] = priv->ftp_rqfcr[i]; 7128c2ecf20Sopenharmony_ci j--; 7138c2ecf20Sopenharmony_ci if ((priv->ftp_rqfcr[i] == 7148c2ecf20Sopenharmony_ci (RQFCR_PID_PARSE | RQFCR_CLE | RQFCR_AND)) && 7158c2ecf20Sopenharmony_ci (priv->ftp_rqfpr[i] == cmp_rqfpr)) 7168c2ecf20Sopenharmony_ci break; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (i == MAX_FILER_IDX + 1) { 7208c2ecf20Sopenharmony_ci netdev_err(priv->ndev, 7218c2ecf20Sopenharmony_ci "No parse rule found, can't create hash rules\n"); 7228c2ecf20Sopenharmony_ci ret = 0; 7238c2ecf20Sopenharmony_ci goto err; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* If a match was found, then it begins the starting of a cluster rule 7278c2ecf20Sopenharmony_ci * if it was already programmed, we need to overwrite these rules 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci for (l = i+1; l < MAX_FILER_IDX; l++) { 7308c2ecf20Sopenharmony_ci if ((priv->ftp_rqfcr[l] & RQFCR_CLE) && 7318c2ecf20Sopenharmony_ci !(priv->ftp_rqfcr[l] & RQFCR_AND)) { 7328c2ecf20Sopenharmony_ci priv->ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT | 7338c2ecf20Sopenharmony_ci RQFCR_HASHTBL_0 | RQFCR_PID_MASK; 7348c2ecf20Sopenharmony_ci priv->ftp_rqfpr[l] = FPR_FILER_MASK; 7358c2ecf20Sopenharmony_ci gfar_write_filer(priv, l, priv->ftp_rqfcr[l], 7368c2ecf20Sopenharmony_ci priv->ftp_rqfpr[l]); 7378c2ecf20Sopenharmony_ci break; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (!(priv->ftp_rqfcr[l] & RQFCR_CLE) && 7418c2ecf20Sopenharmony_ci (priv->ftp_rqfcr[l] & RQFCR_AND)) 7428c2ecf20Sopenharmony_ci continue; 7438c2ecf20Sopenharmony_ci else { 7448c2ecf20Sopenharmony_ci local_rqfpr[j] = priv->ftp_rqfpr[l]; 7458c2ecf20Sopenharmony_ci local_rqfcr[j] = priv->ftp_rqfcr[l]; 7468c2ecf20Sopenharmony_ci j--; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci priv->cur_filer_idx = l - 1; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* hash rules */ 7538c2ecf20Sopenharmony_ci ethflow_to_filer_rules(priv, ethflow); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* Write back the popped out rules again */ 7568c2ecf20Sopenharmony_ci for (k = j+1; k < MAX_FILER_IDX; k++) { 7578c2ecf20Sopenharmony_ci priv->ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k]; 7588c2ecf20Sopenharmony_ci priv->ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k]; 7598c2ecf20Sopenharmony_ci gfar_write_filer(priv, priv->cur_filer_idx, 7608c2ecf20Sopenharmony_ci local_rqfcr[k], local_rqfpr[k]); 7618c2ecf20Sopenharmony_ci if (!priv->cur_filer_idx) 7628c2ecf20Sopenharmony_ci break; 7638c2ecf20Sopenharmony_ci priv->cur_filer_idx = priv->cur_filer_idx - 1; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cierr: 7678c2ecf20Sopenharmony_ci kfree(local_rqfcr); 7688c2ecf20Sopenharmony_ci kfree(local_rqfpr); 7698c2ecf20Sopenharmony_ci return ret; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic int gfar_set_hash_opts(struct gfar_private *priv, 7738c2ecf20Sopenharmony_ci struct ethtool_rxnfc *cmd) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci /* write the filer rules here */ 7768c2ecf20Sopenharmony_ci if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type)) 7778c2ecf20Sopenharmony_ci return -EINVAL; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci return 0; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic int gfar_check_filer_hardware(struct gfar_private *priv) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct gfar __iomem *regs = priv->gfargrp[0].regs; 7858c2ecf20Sopenharmony_ci u32 i; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* Check if we are in FIFO mode */ 7888c2ecf20Sopenharmony_ci i = gfar_read(®s->ecntrl); 7898c2ecf20Sopenharmony_ci i &= ECNTRL_FIFM; 7908c2ecf20Sopenharmony_ci if (i == ECNTRL_FIFM) { 7918c2ecf20Sopenharmony_ci netdev_notice(priv->ndev, "Interface in FIFO mode\n"); 7928c2ecf20Sopenharmony_ci i = gfar_read(®s->rctrl); 7938c2ecf20Sopenharmony_ci i &= RCTRL_PRSDEP_MASK | RCTRL_PRSFM; 7948c2ecf20Sopenharmony_ci if (i == (RCTRL_PRSDEP_MASK | RCTRL_PRSFM)) { 7958c2ecf20Sopenharmony_ci netdev_info(priv->ndev, 7968c2ecf20Sopenharmony_ci "Receive Queue Filtering enabled\n"); 7978c2ecf20Sopenharmony_ci } else { 7988c2ecf20Sopenharmony_ci netdev_warn(priv->ndev, 7998c2ecf20Sopenharmony_ci "Receive Queue Filtering disabled\n"); 8008c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci /* Or in standard mode */ 8048c2ecf20Sopenharmony_ci else { 8058c2ecf20Sopenharmony_ci i = gfar_read(®s->rctrl); 8068c2ecf20Sopenharmony_ci i &= RCTRL_PRSDEP_MASK; 8078c2ecf20Sopenharmony_ci if (i == RCTRL_PRSDEP_MASK) { 8088c2ecf20Sopenharmony_ci netdev_info(priv->ndev, 8098c2ecf20Sopenharmony_ci "Receive Queue Filtering enabled\n"); 8108c2ecf20Sopenharmony_ci } else { 8118c2ecf20Sopenharmony_ci netdev_warn(priv->ndev, 8128c2ecf20Sopenharmony_ci "Receive Queue Filtering disabled\n"); 8138c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* Sets the properties for arbitrary filer rule 8188c2ecf20Sopenharmony_ci * to the first 4 Layer 4 Bytes 8198c2ecf20Sopenharmony_ci */ 8208c2ecf20Sopenharmony_ci gfar_write(®s->rbifx, 0xC0C1C2C3); 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci/* Write a mask to filer cache */ 8258c2ecf20Sopenharmony_cistatic void gfar_set_mask(u32 mask, struct filer_table *tab) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci tab->fe[tab->index].ctrl = RQFCR_AND | RQFCR_PID_MASK | RQFCR_CMP_EXACT; 8288c2ecf20Sopenharmony_ci tab->fe[tab->index].prop = mask; 8298c2ecf20Sopenharmony_ci tab->index++; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci/* Sets parse bits (e.g. IP or TCP) */ 8338c2ecf20Sopenharmony_cistatic void gfar_set_parse_bits(u32 value, u32 mask, struct filer_table *tab) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci gfar_set_mask(mask, tab); 8368c2ecf20Sopenharmony_ci tab->fe[tab->index].ctrl = RQFCR_CMP_EXACT | RQFCR_PID_PARSE | 8378c2ecf20Sopenharmony_ci RQFCR_AND; 8388c2ecf20Sopenharmony_ci tab->fe[tab->index].prop = value; 8398c2ecf20Sopenharmony_ci tab->index++; 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_cistatic void gfar_set_general_attribute(u32 value, u32 mask, u32 flag, 8438c2ecf20Sopenharmony_ci struct filer_table *tab) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci gfar_set_mask(mask, tab); 8468c2ecf20Sopenharmony_ci tab->fe[tab->index].ctrl = RQFCR_CMP_EXACT | RQFCR_AND | flag; 8478c2ecf20Sopenharmony_ci tab->fe[tab->index].prop = value; 8488c2ecf20Sopenharmony_ci tab->index++; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci/* For setting a tuple of value and mask of type flag 8528c2ecf20Sopenharmony_ci * Example: 8538c2ecf20Sopenharmony_ci * IP-Src = 10.0.0.0/255.0.0.0 8548c2ecf20Sopenharmony_ci * value: 0x0A000000 mask: FF000000 flag: RQFPR_IPV4 8558c2ecf20Sopenharmony_ci * 8568c2ecf20Sopenharmony_ci * Ethtool gives us a value=0 and mask=~0 for don't care a tuple 8578c2ecf20Sopenharmony_ci * For a don't care mask it gives us a 0 8588c2ecf20Sopenharmony_ci * 8598c2ecf20Sopenharmony_ci * The check if don't care and the mask adjustment if mask=0 is done for VLAN 8608c2ecf20Sopenharmony_ci * and MAC stuff on an upper level (due to missing information on this level). 8618c2ecf20Sopenharmony_ci * For these guys we can discard them if they are value=0 and mask=0. 8628c2ecf20Sopenharmony_ci * 8638c2ecf20Sopenharmony_ci * Further the all masks are one-padded for better hardware efficiency. 8648c2ecf20Sopenharmony_ci */ 8658c2ecf20Sopenharmony_cistatic void gfar_set_attribute(u32 value, u32 mask, u32 flag, 8668c2ecf20Sopenharmony_ci struct filer_table *tab) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci switch (flag) { 8698c2ecf20Sopenharmony_ci /* 3bit */ 8708c2ecf20Sopenharmony_ci case RQFCR_PID_PRI: 8718c2ecf20Sopenharmony_ci if (!(value | mask)) 8728c2ecf20Sopenharmony_ci return; 8738c2ecf20Sopenharmony_ci mask |= RQFCR_PID_PRI_MASK; 8748c2ecf20Sopenharmony_ci break; 8758c2ecf20Sopenharmony_ci /* 8bit */ 8768c2ecf20Sopenharmony_ci case RQFCR_PID_L4P: 8778c2ecf20Sopenharmony_ci case RQFCR_PID_TOS: 8788c2ecf20Sopenharmony_ci if (!~(mask | RQFCR_PID_L4P_MASK)) 8798c2ecf20Sopenharmony_ci return; 8808c2ecf20Sopenharmony_ci if (!mask) 8818c2ecf20Sopenharmony_ci mask = ~0; 8828c2ecf20Sopenharmony_ci else 8838c2ecf20Sopenharmony_ci mask |= RQFCR_PID_L4P_MASK; 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci /* 12bit */ 8868c2ecf20Sopenharmony_ci case RQFCR_PID_VID: 8878c2ecf20Sopenharmony_ci if (!(value | mask)) 8888c2ecf20Sopenharmony_ci return; 8898c2ecf20Sopenharmony_ci mask |= RQFCR_PID_VID_MASK; 8908c2ecf20Sopenharmony_ci break; 8918c2ecf20Sopenharmony_ci /* 16bit */ 8928c2ecf20Sopenharmony_ci case RQFCR_PID_DPT: 8938c2ecf20Sopenharmony_ci case RQFCR_PID_SPT: 8948c2ecf20Sopenharmony_ci case RQFCR_PID_ETY: 8958c2ecf20Sopenharmony_ci if (!~(mask | RQFCR_PID_PORT_MASK)) 8968c2ecf20Sopenharmony_ci return; 8978c2ecf20Sopenharmony_ci if (!mask) 8988c2ecf20Sopenharmony_ci mask = ~0; 8998c2ecf20Sopenharmony_ci else 9008c2ecf20Sopenharmony_ci mask |= RQFCR_PID_PORT_MASK; 9018c2ecf20Sopenharmony_ci break; 9028c2ecf20Sopenharmony_ci /* 24bit */ 9038c2ecf20Sopenharmony_ci case RQFCR_PID_DAH: 9048c2ecf20Sopenharmony_ci case RQFCR_PID_DAL: 9058c2ecf20Sopenharmony_ci case RQFCR_PID_SAH: 9068c2ecf20Sopenharmony_ci case RQFCR_PID_SAL: 9078c2ecf20Sopenharmony_ci if (!(value | mask)) 9088c2ecf20Sopenharmony_ci return; 9098c2ecf20Sopenharmony_ci mask |= RQFCR_PID_MAC_MASK; 9108c2ecf20Sopenharmony_ci break; 9118c2ecf20Sopenharmony_ci /* for all real 32bit masks */ 9128c2ecf20Sopenharmony_ci default: 9138c2ecf20Sopenharmony_ci if (!~mask) 9148c2ecf20Sopenharmony_ci return; 9158c2ecf20Sopenharmony_ci if (!mask) 9168c2ecf20Sopenharmony_ci mask = ~0; 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci gfar_set_general_attribute(value, mask, flag, tab); 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci/* Translates value and mask for UDP, TCP or SCTP */ 9238c2ecf20Sopenharmony_cistatic void gfar_set_basic_ip(struct ethtool_tcpip4_spec *value, 9248c2ecf20Sopenharmony_ci struct ethtool_tcpip4_spec *mask, 9258c2ecf20Sopenharmony_ci struct filer_table *tab) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->ip4src), 9288c2ecf20Sopenharmony_ci be32_to_cpu(mask->ip4src), 9298c2ecf20Sopenharmony_ci RQFCR_PID_SIA, tab); 9308c2ecf20Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->ip4dst), 9318c2ecf20Sopenharmony_ci be32_to_cpu(mask->ip4dst), 9328c2ecf20Sopenharmony_ci RQFCR_PID_DIA, tab); 9338c2ecf20Sopenharmony_ci gfar_set_attribute(be16_to_cpu(value->pdst), 9348c2ecf20Sopenharmony_ci be16_to_cpu(mask->pdst), 9358c2ecf20Sopenharmony_ci RQFCR_PID_DPT, tab); 9368c2ecf20Sopenharmony_ci gfar_set_attribute(be16_to_cpu(value->psrc), 9378c2ecf20Sopenharmony_ci be16_to_cpu(mask->psrc), 9388c2ecf20Sopenharmony_ci RQFCR_PID_SPT, tab); 9398c2ecf20Sopenharmony_ci gfar_set_attribute(value->tos, mask->tos, RQFCR_PID_TOS, tab); 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/* Translates value and mask for RAW-IP4 */ 9438c2ecf20Sopenharmony_cistatic void gfar_set_user_ip(struct ethtool_usrip4_spec *value, 9448c2ecf20Sopenharmony_ci struct ethtool_usrip4_spec *mask, 9458c2ecf20Sopenharmony_ci struct filer_table *tab) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->ip4src), 9488c2ecf20Sopenharmony_ci be32_to_cpu(mask->ip4src), 9498c2ecf20Sopenharmony_ci RQFCR_PID_SIA, tab); 9508c2ecf20Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->ip4dst), 9518c2ecf20Sopenharmony_ci be32_to_cpu(mask->ip4dst), 9528c2ecf20Sopenharmony_ci RQFCR_PID_DIA, tab); 9538c2ecf20Sopenharmony_ci gfar_set_attribute(value->tos, mask->tos, RQFCR_PID_TOS, tab); 9548c2ecf20Sopenharmony_ci gfar_set_attribute(value->proto, mask->proto, RQFCR_PID_L4P, tab); 9558c2ecf20Sopenharmony_ci gfar_set_attribute(be32_to_cpu(value->l4_4_bytes), 9568c2ecf20Sopenharmony_ci be32_to_cpu(mask->l4_4_bytes), 9578c2ecf20Sopenharmony_ci RQFCR_PID_ARB, tab); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci/* Translates value and mask for ETHER spec */ 9628c2ecf20Sopenharmony_cistatic void gfar_set_ether(struct ethhdr *value, struct ethhdr *mask, 9638c2ecf20Sopenharmony_ci struct filer_table *tab) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci u32 upper_temp_mask = 0; 9668c2ecf20Sopenharmony_ci u32 lower_temp_mask = 0; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* Source address */ 9698c2ecf20Sopenharmony_ci if (!is_broadcast_ether_addr(mask->h_source)) { 9708c2ecf20Sopenharmony_ci if (is_zero_ether_addr(mask->h_source)) { 9718c2ecf20Sopenharmony_ci upper_temp_mask = 0xFFFFFFFF; 9728c2ecf20Sopenharmony_ci lower_temp_mask = 0xFFFFFFFF; 9738c2ecf20Sopenharmony_ci } else { 9748c2ecf20Sopenharmony_ci upper_temp_mask = mask->h_source[0] << 16 | 9758c2ecf20Sopenharmony_ci mask->h_source[1] << 8 | 9768c2ecf20Sopenharmony_ci mask->h_source[2]; 9778c2ecf20Sopenharmony_ci lower_temp_mask = mask->h_source[3] << 16 | 9788c2ecf20Sopenharmony_ci mask->h_source[4] << 8 | 9798c2ecf20Sopenharmony_ci mask->h_source[5]; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci /* Upper 24bit */ 9828c2ecf20Sopenharmony_ci gfar_set_attribute(value->h_source[0] << 16 | 9838c2ecf20Sopenharmony_ci value->h_source[1] << 8 | 9848c2ecf20Sopenharmony_ci value->h_source[2], 9858c2ecf20Sopenharmony_ci upper_temp_mask, RQFCR_PID_SAH, tab); 9868c2ecf20Sopenharmony_ci /* And the same for the lower part */ 9878c2ecf20Sopenharmony_ci gfar_set_attribute(value->h_source[3] << 16 | 9888c2ecf20Sopenharmony_ci value->h_source[4] << 8 | 9898c2ecf20Sopenharmony_ci value->h_source[5], 9908c2ecf20Sopenharmony_ci lower_temp_mask, RQFCR_PID_SAL, tab); 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci /* Destination address */ 9938c2ecf20Sopenharmony_ci if (!is_broadcast_ether_addr(mask->h_dest)) { 9948c2ecf20Sopenharmony_ci /* Special for destination is limited broadcast */ 9958c2ecf20Sopenharmony_ci if ((is_broadcast_ether_addr(value->h_dest) && 9968c2ecf20Sopenharmony_ci is_zero_ether_addr(mask->h_dest))) { 9978c2ecf20Sopenharmony_ci gfar_set_parse_bits(RQFPR_EBC, RQFPR_EBC, tab); 9988c2ecf20Sopenharmony_ci } else { 9998c2ecf20Sopenharmony_ci if (is_zero_ether_addr(mask->h_dest)) { 10008c2ecf20Sopenharmony_ci upper_temp_mask = 0xFFFFFFFF; 10018c2ecf20Sopenharmony_ci lower_temp_mask = 0xFFFFFFFF; 10028c2ecf20Sopenharmony_ci } else { 10038c2ecf20Sopenharmony_ci upper_temp_mask = mask->h_dest[0] << 16 | 10048c2ecf20Sopenharmony_ci mask->h_dest[1] << 8 | 10058c2ecf20Sopenharmony_ci mask->h_dest[2]; 10068c2ecf20Sopenharmony_ci lower_temp_mask = mask->h_dest[3] << 16 | 10078c2ecf20Sopenharmony_ci mask->h_dest[4] << 8 | 10088c2ecf20Sopenharmony_ci mask->h_dest[5]; 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* Upper 24bit */ 10128c2ecf20Sopenharmony_ci gfar_set_attribute(value->h_dest[0] << 16 | 10138c2ecf20Sopenharmony_ci value->h_dest[1] << 8 | 10148c2ecf20Sopenharmony_ci value->h_dest[2], 10158c2ecf20Sopenharmony_ci upper_temp_mask, RQFCR_PID_DAH, tab); 10168c2ecf20Sopenharmony_ci /* And the same for the lower part */ 10178c2ecf20Sopenharmony_ci gfar_set_attribute(value->h_dest[3] << 16 | 10188c2ecf20Sopenharmony_ci value->h_dest[4] << 8 | 10198c2ecf20Sopenharmony_ci value->h_dest[5], 10208c2ecf20Sopenharmony_ci lower_temp_mask, RQFCR_PID_DAL, tab); 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci gfar_set_attribute(be16_to_cpu(value->h_proto), 10258c2ecf20Sopenharmony_ci be16_to_cpu(mask->h_proto), 10268c2ecf20Sopenharmony_ci RQFCR_PID_ETY, tab); 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic inline u32 vlan_tci_vid(struct ethtool_rx_flow_spec *rule) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci return be16_to_cpu(rule->h_ext.vlan_tci) & VLAN_VID_MASK; 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_cistatic inline u32 vlan_tci_vidm(struct ethtool_rx_flow_spec *rule) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci return be16_to_cpu(rule->m_ext.vlan_tci) & VLAN_VID_MASK; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic inline u32 vlan_tci_cfi(struct ethtool_rx_flow_spec *rule) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci return be16_to_cpu(rule->h_ext.vlan_tci) & VLAN_CFI_MASK; 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic inline u32 vlan_tci_cfim(struct ethtool_rx_flow_spec *rule) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci return be16_to_cpu(rule->m_ext.vlan_tci) & VLAN_CFI_MASK; 10478c2ecf20Sopenharmony_ci} 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cistatic inline u32 vlan_tci_prio(struct ethtool_rx_flow_spec *rule) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci return (be16_to_cpu(rule->h_ext.vlan_tci) & VLAN_PRIO_MASK) >> 10528c2ecf20Sopenharmony_ci VLAN_PRIO_SHIFT; 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic inline u32 vlan_tci_priom(struct ethtool_rx_flow_spec *rule) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci return (be16_to_cpu(rule->m_ext.vlan_tci) & VLAN_PRIO_MASK) >> 10588c2ecf20Sopenharmony_ci VLAN_PRIO_SHIFT; 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci/* Convert a rule to binary filter format of gianfar */ 10628c2ecf20Sopenharmony_cistatic int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule, 10638c2ecf20Sopenharmony_ci struct filer_table *tab) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci u32 vlan = 0, vlan_mask = 0; 10668c2ecf20Sopenharmony_ci u32 id = 0, id_mask = 0; 10678c2ecf20Sopenharmony_ci u32 cfi = 0, cfi_mask = 0; 10688c2ecf20Sopenharmony_ci u32 prio = 0, prio_mask = 0; 10698c2ecf20Sopenharmony_ci u32 old_index = tab->index; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* Check if vlan is wanted */ 10728c2ecf20Sopenharmony_ci if ((rule->flow_type & FLOW_EXT) && 10738c2ecf20Sopenharmony_ci (rule->m_ext.vlan_tci != cpu_to_be16(0xFFFF))) { 10748c2ecf20Sopenharmony_ci if (!rule->m_ext.vlan_tci) 10758c2ecf20Sopenharmony_ci rule->m_ext.vlan_tci = cpu_to_be16(0xFFFF); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci vlan = RQFPR_VLN; 10788c2ecf20Sopenharmony_ci vlan_mask = RQFPR_VLN; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* Separate the fields */ 10818c2ecf20Sopenharmony_ci id = vlan_tci_vid(rule); 10828c2ecf20Sopenharmony_ci id_mask = vlan_tci_vidm(rule); 10838c2ecf20Sopenharmony_ci cfi = vlan_tci_cfi(rule); 10848c2ecf20Sopenharmony_ci cfi_mask = vlan_tci_cfim(rule); 10858c2ecf20Sopenharmony_ci prio = vlan_tci_prio(rule); 10868c2ecf20Sopenharmony_ci prio_mask = vlan_tci_priom(rule); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (cfi_mask) { 10898c2ecf20Sopenharmony_ci if (cfi) 10908c2ecf20Sopenharmony_ci vlan |= RQFPR_CFI; 10918c2ecf20Sopenharmony_ci vlan_mask |= RQFPR_CFI; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci switch (rule->flow_type & ~FLOW_EXT) { 10968c2ecf20Sopenharmony_ci case TCP_V4_FLOW: 10978c2ecf20Sopenharmony_ci gfar_set_parse_bits(RQFPR_IPV4 | RQFPR_TCP | vlan, 10988c2ecf20Sopenharmony_ci RQFPR_IPV4 | RQFPR_TCP | vlan_mask, tab); 10998c2ecf20Sopenharmony_ci gfar_set_basic_ip(&rule->h_u.tcp_ip4_spec, 11008c2ecf20Sopenharmony_ci &rule->m_u.tcp_ip4_spec, tab); 11018c2ecf20Sopenharmony_ci break; 11028c2ecf20Sopenharmony_ci case UDP_V4_FLOW: 11038c2ecf20Sopenharmony_ci gfar_set_parse_bits(RQFPR_IPV4 | RQFPR_UDP | vlan, 11048c2ecf20Sopenharmony_ci RQFPR_IPV4 | RQFPR_UDP | vlan_mask, tab); 11058c2ecf20Sopenharmony_ci gfar_set_basic_ip(&rule->h_u.udp_ip4_spec, 11068c2ecf20Sopenharmony_ci &rule->m_u.udp_ip4_spec, tab); 11078c2ecf20Sopenharmony_ci break; 11088c2ecf20Sopenharmony_ci case SCTP_V4_FLOW: 11098c2ecf20Sopenharmony_ci gfar_set_parse_bits(RQFPR_IPV4 | vlan, RQFPR_IPV4 | vlan_mask, 11108c2ecf20Sopenharmony_ci tab); 11118c2ecf20Sopenharmony_ci gfar_set_attribute(132, 0, RQFCR_PID_L4P, tab); 11128c2ecf20Sopenharmony_ci gfar_set_basic_ip((struct ethtool_tcpip4_spec *)&rule->h_u, 11138c2ecf20Sopenharmony_ci (struct ethtool_tcpip4_spec *)&rule->m_u, 11148c2ecf20Sopenharmony_ci tab); 11158c2ecf20Sopenharmony_ci break; 11168c2ecf20Sopenharmony_ci case IP_USER_FLOW: 11178c2ecf20Sopenharmony_ci gfar_set_parse_bits(RQFPR_IPV4 | vlan, RQFPR_IPV4 | vlan_mask, 11188c2ecf20Sopenharmony_ci tab); 11198c2ecf20Sopenharmony_ci gfar_set_user_ip((struct ethtool_usrip4_spec *) &rule->h_u, 11208c2ecf20Sopenharmony_ci (struct ethtool_usrip4_spec *) &rule->m_u, 11218c2ecf20Sopenharmony_ci tab); 11228c2ecf20Sopenharmony_ci break; 11238c2ecf20Sopenharmony_ci case ETHER_FLOW: 11248c2ecf20Sopenharmony_ci if (vlan) 11258c2ecf20Sopenharmony_ci gfar_set_parse_bits(vlan, vlan_mask, tab); 11268c2ecf20Sopenharmony_ci gfar_set_ether((struct ethhdr *) &rule->h_u, 11278c2ecf20Sopenharmony_ci (struct ethhdr *) &rule->m_u, tab); 11288c2ecf20Sopenharmony_ci break; 11298c2ecf20Sopenharmony_ci default: 11308c2ecf20Sopenharmony_ci return -1; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci /* Set the vlan attributes in the end */ 11348c2ecf20Sopenharmony_ci if (vlan) { 11358c2ecf20Sopenharmony_ci gfar_set_attribute(id, id_mask, RQFCR_PID_VID, tab); 11368c2ecf20Sopenharmony_ci gfar_set_attribute(prio, prio_mask, RQFCR_PID_PRI, tab); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* If there has been nothing written till now, it must be a default */ 11408c2ecf20Sopenharmony_ci if (tab->index == old_index) { 11418c2ecf20Sopenharmony_ci gfar_set_mask(0xFFFFFFFF, tab); 11428c2ecf20Sopenharmony_ci tab->fe[tab->index].ctrl = 0x20; 11438c2ecf20Sopenharmony_ci tab->fe[tab->index].prop = 0x0; 11448c2ecf20Sopenharmony_ci tab->index++; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci /* Remove last AND */ 11488c2ecf20Sopenharmony_ci tab->fe[tab->index - 1].ctrl &= (~RQFCR_AND); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci /* Specify which queue to use or to drop */ 11518c2ecf20Sopenharmony_ci if (rule->ring_cookie == RX_CLS_FLOW_DISC) 11528c2ecf20Sopenharmony_ci tab->fe[tab->index - 1].ctrl |= RQFCR_RJE; 11538c2ecf20Sopenharmony_ci else 11548c2ecf20Sopenharmony_ci tab->fe[tab->index - 1].ctrl |= (rule->ring_cookie << 10); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci /* Only big enough entries can be clustered */ 11578c2ecf20Sopenharmony_ci if (tab->index > (old_index + 2)) { 11588c2ecf20Sopenharmony_ci tab->fe[old_index + 1].ctrl |= RQFCR_CLE; 11598c2ecf20Sopenharmony_ci tab->fe[tab->index - 1].ctrl |= RQFCR_CLE; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* In rare cases the cache can be full while there is 11638c2ecf20Sopenharmony_ci * free space in hw 11648c2ecf20Sopenharmony_ci */ 11658c2ecf20Sopenharmony_ci if (tab->index > MAX_FILER_CACHE_IDX - 1) 11668c2ecf20Sopenharmony_ci return -EBUSY; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci return 0; 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci/* Write the bit-pattern from software's buffer to hardware registers */ 11728c2ecf20Sopenharmony_cistatic int gfar_write_filer_table(struct gfar_private *priv, 11738c2ecf20Sopenharmony_ci struct filer_table *tab) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci u32 i = 0; 11768c2ecf20Sopenharmony_ci if (tab->index > MAX_FILER_IDX - 1) 11778c2ecf20Sopenharmony_ci return -EBUSY; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci /* Fill regular entries */ 11808c2ecf20Sopenharmony_ci for (; i < MAX_FILER_IDX && (tab->fe[i].ctrl | tab->fe[i].prop); i++) 11818c2ecf20Sopenharmony_ci gfar_write_filer(priv, i, tab->fe[i].ctrl, tab->fe[i].prop); 11828c2ecf20Sopenharmony_ci /* Fill the rest with fall-troughs */ 11838c2ecf20Sopenharmony_ci for (; i < MAX_FILER_IDX; i++) 11848c2ecf20Sopenharmony_ci gfar_write_filer(priv, i, 0x60, 0xFFFFFFFF); 11858c2ecf20Sopenharmony_ci /* Last entry must be default accept 11868c2ecf20Sopenharmony_ci * because that's what people expect 11878c2ecf20Sopenharmony_ci */ 11888c2ecf20Sopenharmony_ci gfar_write_filer(priv, i, 0x20, 0x0); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci return 0; 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic int gfar_check_capability(struct ethtool_rx_flow_spec *flow, 11948c2ecf20Sopenharmony_ci struct gfar_private *priv) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (flow->flow_type & FLOW_EXT) { 11988c2ecf20Sopenharmony_ci if (~flow->m_ext.data[0] || ~flow->m_ext.data[1]) 11998c2ecf20Sopenharmony_ci netdev_warn(priv->ndev, 12008c2ecf20Sopenharmony_ci "User-specific data not supported!\n"); 12018c2ecf20Sopenharmony_ci if (~flow->m_ext.vlan_etype) 12028c2ecf20Sopenharmony_ci netdev_warn(priv->ndev, 12038c2ecf20Sopenharmony_ci "VLAN-etype not supported!\n"); 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci if (flow->flow_type == IP_USER_FLOW) 12068c2ecf20Sopenharmony_ci if (flow->h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4) 12078c2ecf20Sopenharmony_ci netdev_warn(priv->ndev, 12088c2ecf20Sopenharmony_ci "IP-Version differing from IPv4 not supported!\n"); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci return 0; 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic int gfar_process_filer_changes(struct gfar_private *priv) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci struct ethtool_flow_spec_container *j; 12168c2ecf20Sopenharmony_ci struct filer_table *tab; 12178c2ecf20Sopenharmony_ci s32 ret = 0; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci /* So index is set to zero, too! */ 12208c2ecf20Sopenharmony_ci tab = kzalloc(sizeof(*tab), GFP_KERNEL); 12218c2ecf20Sopenharmony_ci if (tab == NULL) 12228c2ecf20Sopenharmony_ci return -ENOMEM; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci /* Now convert the existing filer data from flow_spec into 12258c2ecf20Sopenharmony_ci * filer tables binary format 12268c2ecf20Sopenharmony_ci */ 12278c2ecf20Sopenharmony_ci list_for_each_entry(j, &priv->rx_list.list, list) { 12288c2ecf20Sopenharmony_ci ret = gfar_convert_to_filer(&j->fs, tab); 12298c2ecf20Sopenharmony_ci if (ret == -EBUSY) { 12308c2ecf20Sopenharmony_ci netdev_err(priv->ndev, 12318c2ecf20Sopenharmony_ci "Rule not added: No free space!\n"); 12328c2ecf20Sopenharmony_ci goto end; 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci if (ret == -1) { 12358c2ecf20Sopenharmony_ci netdev_err(priv->ndev, 12368c2ecf20Sopenharmony_ci "Rule not added: Unsupported Flow-type!\n"); 12378c2ecf20Sopenharmony_ci goto end; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci /* Write everything to hardware */ 12428c2ecf20Sopenharmony_ci ret = gfar_write_filer_table(priv, tab); 12438c2ecf20Sopenharmony_ci if (ret == -EBUSY) { 12448c2ecf20Sopenharmony_ci netdev_err(priv->ndev, "Rule not added: No free space!\n"); 12458c2ecf20Sopenharmony_ci goto end; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ciend: 12498c2ecf20Sopenharmony_ci kfree(tab); 12508c2ecf20Sopenharmony_ci return ret; 12518c2ecf20Sopenharmony_ci} 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_cistatic void gfar_invert_masks(struct ethtool_rx_flow_spec *flow) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci u32 i = 0; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(flow->m_u); i++) 12588c2ecf20Sopenharmony_ci flow->m_u.hdata[i] ^= 0xFF; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci flow->m_ext.vlan_etype ^= cpu_to_be16(0xFFFF); 12618c2ecf20Sopenharmony_ci flow->m_ext.vlan_tci ^= cpu_to_be16(0xFFFF); 12628c2ecf20Sopenharmony_ci flow->m_ext.data[0] ^= cpu_to_be32(~0); 12638c2ecf20Sopenharmony_ci flow->m_ext.data[1] ^= cpu_to_be32(~0); 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic int gfar_add_cls(struct gfar_private *priv, 12678c2ecf20Sopenharmony_ci struct ethtool_rx_flow_spec *flow) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci struct ethtool_flow_spec_container *temp, *comp; 12708c2ecf20Sopenharmony_ci int ret = 0; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci temp = kmalloc(sizeof(*temp), GFP_KERNEL); 12738c2ecf20Sopenharmony_ci if (temp == NULL) 12748c2ecf20Sopenharmony_ci return -ENOMEM; 12758c2ecf20Sopenharmony_ci memcpy(&temp->fs, flow, sizeof(temp->fs)); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci gfar_invert_masks(&temp->fs); 12788c2ecf20Sopenharmony_ci ret = gfar_check_capability(&temp->fs, priv); 12798c2ecf20Sopenharmony_ci if (ret) 12808c2ecf20Sopenharmony_ci goto clean_mem; 12818c2ecf20Sopenharmony_ci /* Link in the new element at the right @location */ 12828c2ecf20Sopenharmony_ci if (list_empty(&priv->rx_list.list)) { 12838c2ecf20Sopenharmony_ci ret = gfar_check_filer_hardware(priv); 12848c2ecf20Sopenharmony_ci if (ret != 0) 12858c2ecf20Sopenharmony_ci goto clean_mem; 12868c2ecf20Sopenharmony_ci list_add(&temp->list, &priv->rx_list.list); 12878c2ecf20Sopenharmony_ci goto process; 12888c2ecf20Sopenharmony_ci } else { 12898c2ecf20Sopenharmony_ci list_for_each_entry(comp, &priv->rx_list.list, list) { 12908c2ecf20Sopenharmony_ci if (comp->fs.location > flow->location) { 12918c2ecf20Sopenharmony_ci list_add_tail(&temp->list, &comp->list); 12928c2ecf20Sopenharmony_ci goto process; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci if (comp->fs.location == flow->location) { 12958c2ecf20Sopenharmony_ci netdev_err(priv->ndev, 12968c2ecf20Sopenharmony_ci "Rule not added: ID %d not free!\n", 12978c2ecf20Sopenharmony_ci flow->location); 12988c2ecf20Sopenharmony_ci ret = -EBUSY; 12998c2ecf20Sopenharmony_ci goto clean_mem; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci list_add_tail(&temp->list, &priv->rx_list.list); 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ciprocess: 13068c2ecf20Sopenharmony_ci priv->rx_list.count++; 13078c2ecf20Sopenharmony_ci ret = gfar_process_filer_changes(priv); 13088c2ecf20Sopenharmony_ci if (ret) 13098c2ecf20Sopenharmony_ci goto clean_list; 13108c2ecf20Sopenharmony_ci return ret; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ciclean_list: 13138c2ecf20Sopenharmony_ci priv->rx_list.count--; 13148c2ecf20Sopenharmony_ci list_del(&temp->list); 13158c2ecf20Sopenharmony_ciclean_mem: 13168c2ecf20Sopenharmony_ci kfree(temp); 13178c2ecf20Sopenharmony_ci return ret; 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_cistatic int gfar_del_cls(struct gfar_private *priv, u32 loc) 13218c2ecf20Sopenharmony_ci{ 13228c2ecf20Sopenharmony_ci struct ethtool_flow_spec_container *comp; 13238c2ecf20Sopenharmony_ci u32 ret = -EINVAL; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (list_empty(&priv->rx_list.list)) 13268c2ecf20Sopenharmony_ci return ret; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci list_for_each_entry(comp, &priv->rx_list.list, list) { 13298c2ecf20Sopenharmony_ci if (comp->fs.location == loc) { 13308c2ecf20Sopenharmony_ci list_del(&comp->list); 13318c2ecf20Sopenharmony_ci kfree(comp); 13328c2ecf20Sopenharmony_ci priv->rx_list.count--; 13338c2ecf20Sopenharmony_ci gfar_process_filer_changes(priv); 13348c2ecf20Sopenharmony_ci ret = 0; 13358c2ecf20Sopenharmony_ci break; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci return ret; 13408c2ecf20Sopenharmony_ci} 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cistatic int gfar_get_cls(struct gfar_private *priv, struct ethtool_rxnfc *cmd) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci struct ethtool_flow_spec_container *comp; 13458c2ecf20Sopenharmony_ci u32 ret = -EINVAL; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci list_for_each_entry(comp, &priv->rx_list.list, list) { 13488c2ecf20Sopenharmony_ci if (comp->fs.location == cmd->fs.location) { 13498c2ecf20Sopenharmony_ci memcpy(&cmd->fs, &comp->fs, sizeof(cmd->fs)); 13508c2ecf20Sopenharmony_ci gfar_invert_masks(&cmd->fs); 13518c2ecf20Sopenharmony_ci ret = 0; 13528c2ecf20Sopenharmony_ci break; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci return ret; 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic int gfar_get_cls_all(struct gfar_private *priv, 13608c2ecf20Sopenharmony_ci struct ethtool_rxnfc *cmd, u32 *rule_locs) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci struct ethtool_flow_spec_container *comp; 13638c2ecf20Sopenharmony_ci u32 i = 0; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci list_for_each_entry(comp, &priv->rx_list.list, list) { 13668c2ecf20Sopenharmony_ci if (i == cmd->rule_cnt) 13678c2ecf20Sopenharmony_ci return -EMSGSIZE; 13688c2ecf20Sopenharmony_ci rule_locs[i] = comp->fs.location; 13698c2ecf20Sopenharmony_ci i++; 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci cmd->data = MAX_FILER_IDX; 13738c2ecf20Sopenharmony_ci cmd->rule_cnt = i; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci return 0; 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_cistatic int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd) 13798c2ecf20Sopenharmony_ci{ 13808c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 13818c2ecf20Sopenharmony_ci int ret = 0; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (test_bit(GFAR_RESETTING, &priv->state)) 13848c2ecf20Sopenharmony_ci return -EBUSY; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci mutex_lock(&priv->rx_queue_access); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci switch (cmd->cmd) { 13898c2ecf20Sopenharmony_ci case ETHTOOL_SRXFH: 13908c2ecf20Sopenharmony_ci ret = gfar_set_hash_opts(priv, cmd); 13918c2ecf20Sopenharmony_ci break; 13928c2ecf20Sopenharmony_ci case ETHTOOL_SRXCLSRLINS: 13938c2ecf20Sopenharmony_ci if ((cmd->fs.ring_cookie != RX_CLS_FLOW_DISC && 13948c2ecf20Sopenharmony_ci cmd->fs.ring_cookie >= priv->num_rx_queues) || 13958c2ecf20Sopenharmony_ci cmd->fs.location >= MAX_FILER_IDX) { 13968c2ecf20Sopenharmony_ci ret = -EINVAL; 13978c2ecf20Sopenharmony_ci break; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci ret = gfar_add_cls(priv, &cmd->fs); 14008c2ecf20Sopenharmony_ci break; 14018c2ecf20Sopenharmony_ci case ETHTOOL_SRXCLSRLDEL: 14028c2ecf20Sopenharmony_ci ret = gfar_del_cls(priv, cmd->fs.location); 14038c2ecf20Sopenharmony_ci break; 14048c2ecf20Sopenharmony_ci default: 14058c2ecf20Sopenharmony_ci ret = -EINVAL; 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci mutex_unlock(&priv->rx_queue_access); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci return ret; 14118c2ecf20Sopenharmony_ci} 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cistatic int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 14148c2ecf20Sopenharmony_ci u32 *rule_locs) 14158c2ecf20Sopenharmony_ci{ 14168c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 14178c2ecf20Sopenharmony_ci int ret = 0; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci switch (cmd->cmd) { 14208c2ecf20Sopenharmony_ci case ETHTOOL_GRXRINGS: 14218c2ecf20Sopenharmony_ci cmd->data = priv->num_rx_queues; 14228c2ecf20Sopenharmony_ci break; 14238c2ecf20Sopenharmony_ci case ETHTOOL_GRXCLSRLCNT: 14248c2ecf20Sopenharmony_ci cmd->rule_cnt = priv->rx_list.count; 14258c2ecf20Sopenharmony_ci break; 14268c2ecf20Sopenharmony_ci case ETHTOOL_GRXCLSRULE: 14278c2ecf20Sopenharmony_ci ret = gfar_get_cls(priv, cmd); 14288c2ecf20Sopenharmony_ci break; 14298c2ecf20Sopenharmony_ci case ETHTOOL_GRXCLSRLALL: 14308c2ecf20Sopenharmony_ci ret = gfar_get_cls_all(priv, cmd, rule_locs); 14318c2ecf20Sopenharmony_ci break; 14328c2ecf20Sopenharmony_ci default: 14338c2ecf20Sopenharmony_ci ret = -EINVAL; 14348c2ecf20Sopenharmony_ci break; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci return ret; 14388c2ecf20Sopenharmony_ci} 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_cistatic int gfar_get_ts_info(struct net_device *dev, 14418c2ecf20Sopenharmony_ci struct ethtool_ts_info *info) 14428c2ecf20Sopenharmony_ci{ 14438c2ecf20Sopenharmony_ci struct gfar_private *priv = netdev_priv(dev); 14448c2ecf20Sopenharmony_ci struct platform_device *ptp_dev; 14458c2ecf20Sopenharmony_ci struct device_node *ptp_node; 14468c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp = NULL; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci info->phc_index = -1; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)) { 14518c2ecf20Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | 14528c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE; 14538c2ecf20Sopenharmony_ci return 0; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci ptp_node = of_find_compatible_node(NULL, NULL, "fsl,etsec-ptp"); 14578c2ecf20Sopenharmony_ci if (ptp_node) { 14588c2ecf20Sopenharmony_ci ptp_dev = of_find_device_by_node(ptp_node); 14598c2ecf20Sopenharmony_ci of_node_put(ptp_node); 14608c2ecf20Sopenharmony_ci if (ptp_dev) 14618c2ecf20Sopenharmony_ci ptp = platform_get_drvdata(ptp_dev); 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci if (ptp) 14658c2ecf20Sopenharmony_ci info->phc_index = ptp->phc_index; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | 14688c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 14698c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 14708c2ecf20Sopenharmony_ci info->tx_types = (1 << HWTSTAMP_TX_OFF) | 14718c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_ON); 14728c2ecf20Sopenharmony_ci info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 14738c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_ALL); 14748c2ecf20Sopenharmony_ci return 0; 14758c2ecf20Sopenharmony_ci} 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ciconst struct ethtool_ops gfar_ethtool_ops = { 14788c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 14798c2ecf20Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 14808c2ecf20Sopenharmony_ci .get_drvinfo = gfar_gdrvinfo, 14818c2ecf20Sopenharmony_ci .get_regs_len = gfar_reglen, 14828c2ecf20Sopenharmony_ci .get_regs = gfar_get_regs, 14838c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 14848c2ecf20Sopenharmony_ci .get_coalesce = gfar_gcoalesce, 14858c2ecf20Sopenharmony_ci .set_coalesce = gfar_scoalesce, 14868c2ecf20Sopenharmony_ci .get_ringparam = gfar_gringparam, 14878c2ecf20Sopenharmony_ci .set_ringparam = gfar_sringparam, 14888c2ecf20Sopenharmony_ci .get_pauseparam = gfar_gpauseparam, 14898c2ecf20Sopenharmony_ci .set_pauseparam = gfar_spauseparam, 14908c2ecf20Sopenharmony_ci .get_strings = gfar_gstrings, 14918c2ecf20Sopenharmony_ci .get_sset_count = gfar_sset_count, 14928c2ecf20Sopenharmony_ci .get_ethtool_stats = gfar_fill_stats, 14938c2ecf20Sopenharmony_ci .get_msglevel = gfar_get_msglevel, 14948c2ecf20Sopenharmony_ci .set_msglevel = gfar_set_msglevel, 14958c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 14968c2ecf20Sopenharmony_ci .get_wol = gfar_get_wol, 14978c2ecf20Sopenharmony_ci .set_wol = gfar_set_wol, 14988c2ecf20Sopenharmony_ci#endif 14998c2ecf20Sopenharmony_ci .set_rxnfc = gfar_set_nfc, 15008c2ecf20Sopenharmony_ci .get_rxnfc = gfar_get_nfc, 15018c2ecf20Sopenharmony_ci .get_ts_info = gfar_get_ts_info, 15028c2ecf20Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 15038c2ecf20Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 15048c2ecf20Sopenharmony_ci}; 1505