aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/net/dsa/mt7530.txt92
-rw-r--r--drivers/net/dsa/Kconfig8
-rw-r--r--drivers/net/dsa/Makefile1
-rw-r--r--drivers/net/dsa/mt7530.c1125
-rw-r--r--drivers/net/dsa/mt7530.h402
-rw-r--r--drivers/net/ethernet/3com/typhoon.c7
-rw-r--r--drivers/net/ethernet/amd/nmclan_cs.c49
-rw-r--r--drivers/net/ethernet/cadence/macb.c40
-rw-r--r--drivers/net/ethernet/cadence/macb.h1
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_main.c8
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.c10
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/common.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cxgb2.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/adapter.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c2
-rw-r--r--drivers/net/ethernet/dec/tulip/de2104x.c42
-rw-r--r--drivers/net/ethernet/dlink/dl2k.c45
-rw-r--r--drivers/net/ethernet/dlink/dl2k.h1
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.c476
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.h16
-rw-r--r--drivers/net/ethernet/ibm/emac/core.c2
-rw-r--r--drivers/net/ethernet/ibm/emac/core.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_client.c9
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c62
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl.h3
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c7
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h3
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_main.c61
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c4
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.c8
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.h4
-rw-r--r--drivers/net/ethernet/moxa/moxart_ether.c28
-rw-r--r--drivers/net/ethernet/moxa/moxart_ether.h1
-rw-r--r--drivers/net/ethernet/nuvoton/w90p910_ether.c33
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede.h22
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_filter.c10
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_fp.c70
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_main.c14
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/chain_mode.c45
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/ring_mode.c46
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h49
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c1260
-rw-r--r--drivers/net/ethernet/sun/sunbmac.c18
-rw-r--r--drivers/net/ethernet/sun/sunbmac.h1
-rw-r--r--drivers/net/ethernet/sun/sunhme.c22
-rw-r--r--drivers/net/ethernet/sun/sunhme.h2
-rw-r--r--drivers/net/usb/kaweth.c32
-rw-r--r--drivers/net/usb/pegasus.c36
-rw-r--r--drivers/net/usb/pegasus.h1
-rw-r--r--drivers/s390/net/ctcm_fsms.c2
-rw-r--r--drivers/s390/net/ctcm_main.c12
-rw-r--r--drivers/s390/net/netiucv.c2
-rw-r--r--drivers/s390/net/qeth_core.h16
-rw-r--r--drivers/s390/net/qeth_core_main.c35
-rw-r--r--drivers/s390/net/qeth_core_mpc.h17
-rw-r--r--drivers/s390/net/qeth_l2_main.c1
-rw-r--r--drivers/s390/net/qeth_l2_sys.c3
-rw-r--r--drivers/s390/net/qeth_l3_main.c48
-rw-r--r--drivers/s390/net/qeth_l3_sys.c4
-rw-r--r--include/net/dsa.h1
-rw-r--r--net/dsa/Kconfig2
-rw-r--r--net/dsa/Makefile1
-rw-r--r--net/dsa/dsa.c3
-rw-r--r--net/dsa/dsa_priv.h3
-rw-r--r--net/dsa/tag_mtk.c118
-rw-r--r--net/ipv4/route.c13
66 files changed, 3298 insertions, 1166 deletions
diff --git a/Documentation/devicetree/bindings/net/dsa/mt7530.txt b/Documentation/devicetree/bindings/net/dsa/mt7530.txt
new file mode 100644
index 000000000000..a9bc27b93ee3
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/mt7530.txt
@@ -0,0 +1,92 @@
+Mediatek MT7530 Ethernet switch
+================================
+
+Required properties:
+
+- compatible: Must be compatible = "mediatek,mt7530";
+- #address-cells: Must be 1.
+- #size-cells: Must be 0.
+- mediatek,mcm: Boolean; if defined, indicates that either MT7530 is the part
+ on multi-chip module belong to MT7623A has or the remotely standalone
+ chip as the function MT7623N reference board provided for.
+- core-supply: Phandle to the regulator node necessary for the core power.
+- io-supply: Phandle to the regulator node necessary for the I/O power.
+ See Documentation/devicetree/bindings/regulator/mt6323-regulator.txt
+ for details for the regulator setup on these boards.
+
+If the property mediatek,mcm isn't defined, following property is required
+
+- reset-gpios: Should be a gpio specifier for a reset line.
+
+Else, following properties are required
+
+- resets : Phandle pointing to the system reset controller with
+ line index for the ethsys.
+- reset-names : Should be set to "mcm".
+
+Required properties for the child nodes within ports container:
+
+- reg: Port address described must be 6 for CPU port and from 0 to 5 for
+ user ports.
+- phy-mode: String, must be either "trgmii" or "rgmii" for port labeled
+ "cpu".
+
+See Documentation/devicetree/bindings/dsa/dsa.txt for a list of additional
+required, optional properties and how the integrated switch subnodes must
+be specified.
+
+Example:
+
+ &mdio0 {
+ switch@0 {
+ compatible = "mediatek,mt7530";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ core-supply = <&mt6323_vpa_reg>;
+ io-supply = <&mt6323_vemc3v3_reg>;
+ reset-gpios = <&pio 33 0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ port@0 {
+ reg = <0>;
+ label = "lan0";
+ };
+
+ port@1 {
+ reg = <1>;
+ label = "lan1";
+ };
+
+ port@2 {
+ reg = <2>;
+ label = "lan2";
+ };
+
+ port@3 {
+ reg = <3>;
+ label = "lan3";
+ };
+
+ port@4 {
+ reg = <4>;
+ label = "wan";
+ };
+
+ port@6 {
+ reg = <6>;
+ label = "cpu";
+ ethernet = <&gmac0>;
+ phy-mode = "trgmii";
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
+ };
+ };
+ };
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index ba2e655eec19..31a2b229106d 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -42,4 +42,12 @@ config NET_DSA_LOOP
This enables support for a fake mock-up switch chip which
exercises the DSA APIs.
+config NET_DSA_MT7530
+ tristate "Mediatek MT7530 Ethernet switch support"
+ depends on NET_DSA
+ select NET_DSA_TAG_MTK
+ ---help---
+ This enables support for the Mediatek MT7530 Ethernet switch
+ chip.
+
endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 5c8830991041..2ae07f4fbf63 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o
bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o
obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
+obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o
obj-y += b53/
obj-y += mv88e6xxx/
obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
new file mode 100644
index 000000000000..a4f3b0b8c28e
--- /dev/null
+++ b/drivers/net/dsa/mt7530.c
@@ -0,0 +1,1125 @@
+/*
+ * Mediatek MT7530 DSA Switch driver
+ * Copyright (C) 2017 Sean Wang <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/iopoll.h>
+#include <linux/mdio.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_gpio.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+
+#include "mt7530.h"
+
+/* String, offset, and register size in bytes if different from 4 bytes */
+static const struct mt7530_mib_desc mt7530_mib[] = {
+ MIB_DESC(1, 0x00, "TxDrop"),
+ MIB_DESC(1, 0x04, "TxCrcErr"),
+ MIB_DESC(1, 0x08, "TxUnicast"),
+ MIB_DESC(1, 0x0c, "TxMulticast"),
+ MIB_DESC(1, 0x10, "TxBroadcast"),
+ MIB_DESC(1, 0x14, "TxCollision"),
+ MIB_DESC(1, 0x18, "TxSingleCollision"),
+ MIB_DESC(1, 0x1c, "TxMultipleCollision"),
+ MIB_DESC(1, 0x20, "TxDeferred"),
+ MIB_DESC(1, 0x24, "TxLateCollision"),
+ MIB_DESC(1, 0x28, "TxExcessiveCollistion"),
+ MIB_DESC(1, 0x2c, "TxPause"),
+ MIB_DESC(1, 0x30, "TxPktSz64"),
+ MIB_DESC(1, 0x34, "TxPktSz65To127"),
+ MIB_DESC(1, 0x38, "TxPktSz128To255"),
+ MIB_DESC(1, 0x3c, "TxPktSz256To511"),
+ MIB_DESC(1, 0x40, "TxPktSz512To1023"),
+ MIB_DESC(1, 0x44, "Tx1024ToMax"),
+ MIB_DESC(2, 0x48, "TxBytes"),
+ MIB_DESC(1, 0x60, "RxDrop"),
+ MIB_DESC(1, 0x64, "RxFiltering"),
+ MIB_DESC(1, 0x6c, "RxMulticast"),
+ MIB_DESC(1, 0x70, "RxBroadcast"),
+ MIB_DESC(1, 0x74, "RxAlignErr"),
+ MIB_DESC(1, 0x78, "RxCrcErr"),
+ MIB_DESC(1, 0x7c, "RxUnderSizeErr"),
+ MIB_DESC(1, 0x80, "RxFragErr"),
+ MIB_DESC(1, 0x84, "RxOverSzErr"),
+ MIB_DESC(1, 0x88, "RxJabberErr"),
+ MIB_DESC(1, 0x8c, "RxPause"),
+ MIB_DESC(1, 0x90, "RxPktSz64"),
+ MIB_DESC(1, 0x94, "RxPktSz65To127"),
+ MIB_DESC(1, 0x98, "RxPktSz128To255"),
+ MIB_DESC(1, 0x9c, "RxPktSz256To511"),
+ MIB_DESC(1, 0xa0, "RxPktSz512To1023"),
+ MIB_DESC(1, 0xa4, "RxPktSz1024ToMax"),
+ MIB_DESC(2, 0xa8, "RxBytes"),
+ MIB_DESC(1, 0xb0, "RxCtrlDrop"),
+ MIB_DESC(1, 0xb4, "RxIngressDrop"),
+ MIB_DESC(1, 0xb8, "RxArlDrop"),
+};
+
+static int
+mt7623_trgmii_write(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ int ret;
+
+ ret = regmap_write(priv->ethernet, TRGMII_BASE(reg), val);
+ if (ret < 0)
+ dev_err(priv->dev,
+ "failed to priv write register\n");
+ return ret;
+}
+
+static u32
+mt7623_trgmii_read(struct mt7530_priv *priv, u32 reg)
+{
+ int ret;
+ u32 val;
+
+ ret = regmap_read(priv->ethernet, TRGMII_BASE(reg), &val);
+ if (ret < 0) {
+ dev_err(priv->dev,
+ "failed to priv read register\n");
+ return ret;
+ }
+
+ return val;
+}
+
+static void
+mt7623_trgmii_rmw(struct mt7530_priv *priv, u32 reg,
+ u32 mask, u32 set)
+{
+ u32 val;
+
+ val = mt7623_trgmii_read(priv, reg);
+ val &= ~mask;
+ val |= set;
+ mt7623_trgmii_write(priv, reg, val);
+}
+
+static void
+mt7623_trgmii_set(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ mt7623_trgmii_rmw(priv, reg, 0, val);
+}
+
+static void
+mt7623_trgmii_clear(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ mt7623_trgmii_rmw(priv, reg, val, 0);
+}
+
+static int
+core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad)
+{
+ struct mii_bus *bus = priv->bus;
+ int value, ret;
+
+ /* Write the desired MMD Devad */
+ ret = bus->write(bus, 0, MII_MMD_CTRL, devad);
+ if (ret < 0)
+ goto err;
+
+ /* Write the desired MMD register address */
+ ret = bus->write(bus, 0, MII_MMD_DATA, prtad);
+ if (ret < 0)
+ goto err;
+
+ /* Select the Function : DATA with no post increment */
+ ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
+ if (ret < 0)
+ goto err;
+
+ /* Read the content of the MMD's selected register */
+ value = bus->read(bus, 0, MII_MMD_DATA);
+
+ return value;
+err:
+ dev_err(&bus->dev, "failed to read mmd register\n");
+
+ return ret;
+}
+
+static int
+core_write_mmd_indirect(struct mt7530_priv *priv, int prtad,
+ int devad, u32 data)
+{
+ struct mii_bus *bus = priv->bus;
+ int ret;
+
+ /* Write the desired MMD Devad */
+ ret = bus->write(bus, 0, MII_MMD_CTRL, devad);
+ if (ret < 0)
+ goto err;
+
+ /* Write the desired MMD register address */
+ ret = bus->write(bus, 0, MII_MMD_DATA, prtad);
+ if (ret < 0)
+ goto err;
+
+ /* Select the Function : DATA with no post increment */
+ ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
+ if (ret < 0)
+ goto err;
+
+ /* Write the data into MMD's selected register */
+ ret = bus->write(bus, 0, MII_MMD_DATA, data);
+err:
+ if (ret < 0)
+ dev_err(&bus->dev,
+ "failed to write mmd register\n");
+ return ret;
+}
+
+static void
+core_write(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ struct mii_bus *bus = priv->bus;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
+
+ mutex_unlock(&bus->mdio_lock);
+}
+
+static void
+core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)
+{
+ struct mii_bus *bus = priv->bus;
+ u32 val;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2);
+ val &= ~mask;
+ val |= set;
+ core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
+
+ mutex_unlock(&bus->mdio_lock);
+}
+
+static void
+core_set(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ core_rmw(priv, reg, 0, val);
+}
+
+static void
+core_clear(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ core_rmw(priv, reg, val, 0);
+}
+
+static int
+mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ struct mii_bus *bus = priv->bus;
+ u16 page, r, lo, hi;
+ int ret;
+
+ page = (reg >> 6) & 0x3ff;
+ r = (reg >> 2) & 0xf;
+ lo = val & 0xffff;
+ hi = val >> 16;
+
+ /* MT7530 uses 31 as the pseudo port */
+ ret = bus->write(bus, 0x1f, 0x1f, page);
+ if (ret < 0)
+ goto err;
+
+ ret = bus->write(bus, 0x1f, r, lo);
+ if (ret < 0)
+ goto err;
+
+ ret = bus->write(bus, 0x1f, 0x10, hi);
+err:
+ if (ret < 0)
+ dev_err(&bus->dev,
+ "failed to write mt7530 register\n");
+ return ret;
+}
+
+static u32
+mt7530_mii_read(struct mt7530_priv *priv, u32 reg)
+{
+ struct mii_bus *bus = priv->bus;
+ u16 page, r, lo, hi;
+ int ret;
+
+ page = (reg >> 6) & 0x3ff;
+ r = (reg >> 2) & 0xf;
+
+ /* MT7530 uses 31 as the pseudo port */
+ ret = bus->write(bus, 0x1f, 0x1f, page);
+ if (ret < 0) {
+ dev_err(&bus->dev,
+ "failed to read mt7530 register\n");
+ return ret;
+ }
+
+ lo = bus->read(bus, 0x1f, r);
+ hi = bus->read(bus, 0x1f, 0x10);
+
+ return (hi << 16) | (lo & 0xffff);
+}
+
+static void
+mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ struct mii_bus *bus = priv->bus;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ mt7530_mii_write(priv, reg, val);
+
+ mutex_unlock(&bus->mdio_lock);
+}
+
+static u32
+_mt7530_read(struct mt7530_dummy_poll *p)
+{
+ struct mii_bus *bus = p->priv->bus;
+ u32 val;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ val = mt7530_mii_read(p->priv, p->reg);
+
+ mutex_unlock(&bus->mdio_lock);
+
+ return val;
+}
+
+static u32
+mt7530_read(struct mt7530_priv *priv, u32 reg)
+{
+ struct mt7530_dummy_poll p;
+
+ INIT_MT7530_DUMMY_POLL(&p, priv, reg);
+ return _mt7530_read(&p);
+}
+
+static void
+mt7530_rmw(struct mt7530_priv *priv, u32 reg,
+ u32 mask, u32 set)
+{
+ struct mii_bus *bus = priv->bus;
+ u32 val;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ val = mt7530_mii_read(priv, reg);
+ val &= ~mask;
+ val |= set;
+ mt7530_mii_write(priv, reg, val);
+
+ mutex_unlock(&bus->mdio_lock);
+}
+
+static void
+mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ mt7530_rmw(priv, reg, 0, val);
+}
+
+static void
+mt7530_clear(struct mt7530_priv *priv, u32 reg, u32 val)
+{
+ mt7530_rmw(priv, reg, val, 0);
+}
+
+static int
+mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp)
+{
+ u32 val;
+ int ret;
+ struct mt7530_dummy_poll p;
+
+ /* Set the command operating upon the MAC address entries */
+ val = ATC_BUSY | ATC_MAT(0) | cmd;
+ mt7530_write(priv, MT7530_ATC, val);
+
+ INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_ATC);
+ ret = readx_poll_timeout(_mt7530_read, &p, val,
+ !(val & ATC_BUSY), 20, 20000);
+ if (ret < 0) {
+ dev_err(priv->dev, "reset timeout\n");
+ return ret;
+ }
+
+ /* Additional sanity for read command if the specified
+ * entry is invalid
+ */
+ val = mt7530_read(priv, MT7530_ATC);
+ if ((cmd == MT7530_FDB_READ) && (val & ATC_INVALID))
+ return -EINVAL;
+
+ if (rsp)
+ *rsp = val;
+
+ return 0;
+}
+
+static void
+mt7530_fdb_read(struct mt7530_priv *priv, struct mt7530_fdb *fdb)
+{
+ u32 reg[3];
+ int i;
+
+ /* Read from ARL table into an array */
+ for (i = 0; i < 3; i++) {
+ reg[i] = mt7530_read(priv, MT7530_TSRA1 + (i * 4));
+
+ dev_dbg(priv->dev, "%s(%d) reg[%d]=0x%x\n",
+ __func__, __LINE__, i, reg[i]);
+ }
+
+ fdb->vid = (reg[1] >> CVID) & CVID_MASK;
+ fdb->aging = (reg[2] >> AGE_TIMER) & AGE_TIMER_MASK;
+ fdb->port_mask = (reg[2] >> PORT_MAP) & PORT_MAP_MASK;
+ fdb->mac[0] = (reg[0] >> MAC_BYTE_0) & MAC_BYTE_MASK;
+ fdb->mac[1] = (reg[0] >> MAC_BYTE_1) & MAC_BYTE_MASK;
+ fdb->mac[2] = (reg[0] >> MAC_BYTE_2) & MAC_BYTE_MASK;
+ fdb->mac[3] = (reg[0] >> MAC_BYTE_3) & MAC_BYTE_MASK;
+ fdb->mac[4] = (reg[1] >> MAC_BYTE_4) & MAC_BYTE_MASK;
+ fdb->mac[5] = (reg[1] >> MAC_BYTE_5) & MAC_BYTE_MASK;
+ fdb->noarp = ((reg[2] >> ENT_STATUS) & ENT_STATUS_MASK) == STATIC_ENT;
+}
+
+static void
+mt7530_fdb_write(struct mt7530_priv *priv, u16 vid,
+ u8 port_mask, const u8 *mac,
+ u8 aging, u8 type)
+{
+ u32 reg[3] = { 0 };
+ int i;
+
+ reg[1] |= vid & CVID_MASK;
+ reg[2] |= (aging & AGE_TIMER_MASK) << AGE_TIMER;
+ reg[2] |= (port_mask & PORT_MAP_MASK) << PORT_MAP;
+ /* STATIC_ENT indicate that entry is static wouldn't
+ * be aged out and STATIC_EMP specified as erasing an
+ * entry
+ */
+ reg[2] |= (type & ENT_STATUS_MASK) << ENT_STATUS;
+ reg[1] |= mac[5] << MAC_BYTE_5;
+ reg[1] |= mac[4] << MAC_BYTE_4;
+ reg[0] |= mac[3] << MAC_BYTE_3;
+ reg[0] |= mac[2] << MAC_BYTE_2;
+ reg[0] |= mac[1] << MAC_BYTE_1;
+ reg[0] |= mac[0] << MAC_BYTE_0;
+
+ /* Write array into the ARL table */
+ for (i = 0; i < 3; i++)
+ mt7530_write(priv, MT7530_ATA1 + (i * 4), reg[i]);
+}
+
+static int
+mt7530_pad_clk_setup(struct dsa_switch *ds, int mode)
+{
+ struct mt7530_priv *priv = ds->priv;
+ u32 ncpo1, ssc_delta, trgint, i;
+
+ switch (mode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ trgint = 0;
+ ncpo1 = 0x0c80;
+ ssc_delta = 0x87;
+ break;
+ case PHY_INTERFACE_MODE_TRGMII:
+ trgint = 1;
+ ncpo1 = 0x1400;
+ ssc_delta = 0x57;
+ break;
+ default:
+ dev_err(priv->dev, "xMII mode %d not supported\n", mode);
+ return -EINVAL;
+ }
+
+ mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK,
+ P6_INTF_MODE(trgint));
+
+ /* Lower Tx Driving for TRGMII path */
+ for (i = 0 ; i < NUM_TRGMII_CTRL ; i++)
+ mt7530_write(priv, MT7530_TRGMII_TD_ODT(i),
+ TD_DM_DRVP(8) | TD_DM_DRVN(8));
+
+ /* Setup core clock for MT7530 */
+ if (!trgint) {
+ /* Disable MT7530 core clock */
+ core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+
+ /* Disable PLL, since phy_device has not yet been created
+ * provided for phy_[read,write]_mmd_indirect is called, we
+ * provide our own core_write_mmd_indirect to complete this
+ * function.
+ */
+ core_write_mmd_indirect(priv,
+ CORE_GSWPLL_GRP1,
+ MDIO_MMD_VEND2,
+ 0);
+
+ /* Set core clock into 500Mhz */
+ core_write(priv, CORE_GSWPLL_GRP2,
+ RG_GSWPLL_POSDIV_500M(1) |
+ RG_GSWPLL_FBKDIV_500M(25));
+
+ /* Enable PLL */
+ core_write(priv, CORE_GSWPLL_GRP1,
+ RG_GSWPLL_EN_PRE |
+ RG_GSWPLL_POSDIV_200M(2) |
+ RG_GSWPLL_FBKDIV_200M(32));
+
+ /* Enable MT7530 core clock */
+ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+ }
+
+ /* Setup the MT7530 TRGMII Tx Clock */
+ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+ core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1));
+ core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0));
+ core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta));
+ core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta));
+ core_write(priv, CORE_PLL_GROUP4,
+ RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN |
+ RG_SYSPLL_BIAS_LPF_EN);
+ core_write(priv, CORE_PLL_GROUP2,
+ RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN |
+ RG_SYSPLL_POSDIV(1));
+ core_write(priv, CORE_PLL_GROUP7,
+ RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) |
+ RG_LCDDS_PWDB | RG_LCDDS_ISO_EN);
+ core_set(priv, CORE_TRGMII_GSW_CLK_CG,
+ REG_GSWCK_EN | REG_TRGMIICK_EN);
+
+ if (!trgint)
+ for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
+ mt7530_rmw(priv, MT7530_TRGMII_RD(i),
+ RD_TAP_MASK, RD_TAP(16));
+ else
+ mt7623_trgmii_set(priv, GSW_INTF_MODE, INTF_MODE_TRGMII);
+
+ return 0;
+}
+
+static int
+mt7623_pad_clk_setup(struct dsa_switch *ds)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int i;
+
+ for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
+ mt7623_trgmii_write(priv, GSW_TRGMII_TD_ODT(i),
+ TD_DM_DRVP(8) | TD_DM_DRVN(8));
+
+ mt7623_trgmii_set(priv, GSW_TRGMII_RCK_CTRL, RX_RST | RXC_DQSISEL);
+ mt7623_trgmii_clear(priv, GSW_TRGMII_RCK_CTRL, RX_RST);
+
+ return 0;
+}
+
+static void
+mt7530_mib_reset(struct dsa_switch *ds)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_FLUSH);
+ mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_ACTIVATE);
+}
+
+static void
+mt7530_port_set_status(struct mt7530_priv *priv, int port, int enable)
+{
+ u32 mask = PMCR_TX_EN | PMCR_RX_EN;
+
+ if (enable)
+ mt7530_set(priv, MT7530_PMCR_P(port), mask);
+ else
+ mt7530_clear(priv, MT7530_PMCR_P(port), mask);
+}
+
+static int mt7530_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ return mdiobus_read_nested(priv->bus, port, regnum);
+}
+
+int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ return mdiobus_write_nested(priv->bus, port, regnum, val);
+}
+
+static void
+mt7530_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++)
+ strncpy(data + i * ETH_GSTRING_LEN, mt7530_mib[i].name,
+ ETH_GSTRING_LEN);
+}
+
+static void
+mt7530_get_ethtool_stats(struct dsa_switch *ds, int port,
+ uint64_t *data)
+{
+ struct mt7530_priv *priv = ds->priv;
+ const struct mt7530_mib_desc *mib;
+ u32 reg, i;
+ u64 hi;
+
+ for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++) {
+ mib = &mt7530_mib[i];
+ reg = MT7530_PORT_MIB_COUNTER(port) + mib->offset;
+
+ data[i] = mt7530_read(priv, reg);
+ if (mib->size == 2) {
+ hi = mt7530_read(priv, reg + 4);
+ data[i] |= hi << 32;
+ }
+ }
+}
+
+static int
+mt7530_get_sset_count(struct dsa_switch *ds)
+{
+ return ARRAY_SIZE(mt7530_mib);
+}
+
+static void mt7530_adjust_link(struct dsa_switch *ds, int port,
+ struct phy_device *phydev)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ if (phy_is_pseudo_fixed_link(phydev)) {
+ dev_dbg(priv->dev, "phy-mode for master device = %x\n",
+ phydev->interface);
+
+ /* Setup TX circuit incluing relevant PAD and driving */
+ mt7530_pad_clk_setup(ds, phydev->interface);
+
+ /* Setup RX circuit, relevant PAD and driving on the host
+ * which must be placed after the setup on the device side is
+ * all finished.
+ */
+ mt7623_pad_clk_setup(ds);
+ }
+}
+
+static int
+mt7530_cpu_port_enable(struct mt7530_priv *priv,
+ int port)
+{
+ /* Enable Mediatek header mode on the cpu port */
+ mt7530_write(priv, MT7530_PVC_P(port),
+ PORT_SPEC_TAG);
+
+ /* Setup the MAC by default for the cpu port */
+ mt7530_write(priv, MT7530_PMCR_P(port), PMCR_CPUP_LINK);
+
+ /* Disable auto learning on the cpu port */
+ mt7530_set(priv, MT7530_PSC_P(port), SA_DIS);
+
+ /* Unknown unicast frame fordwarding to the cpu port */
+ mt7530_set(priv, MT7530_MFC, UNU_FFP(BIT(port)));
+
+ /* CPU port gets connected to all user ports of
+ * the switch
+ */
+ mt7530_write(priv, MT7530_PCR_P(port),
+ PCR_MATRIX(priv->ds->enabled_port_mask));
+
+ return 0;
+}
+
+static int
+mt7530_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+
+ /* Setup the MAC for the user port */
+ mt7530_write(priv, MT7530_PMCR_P(port), PMCR_USERP_LINK);
+
+ /* Allow the user port gets connected to the cpu port and also
+ * restore the port matrix if the port is the member of a certain
+ * bridge.
+ */
+ priv->ports[port].pm |= PCR_MATRIX(BIT(MT7530_CPU_PORT));
+ priv->ports[port].enable = true;
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+ priv->ports[port].pm);
+ mt7530_port_set_status(priv, port, 1);
+
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static void
+mt7530_port_disable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+
+ /* Clear up all port matrix which could be restored in the next
+ * enablement for the port.
+ */
+ priv->ports[port].enable = false;
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+ PCR_MATRIX_CLR);
+ mt7530_port_set_status(priv, port, 0);
+
+ mutex_unlock(&priv->reg_mutex);
+}
+
+static void
+mt7530_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+ struct mt7530_priv *priv = ds->priv;
+ u32 stp_state;
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ stp_state = MT7530_STP_DISABLED;
+ break;
+ case BR_STATE_BLOCKING:
+ stp_state = MT7530_STP_BLOCKING;
+ break;
+ case BR_STATE_LISTENING:
+ stp_state = MT7530_STP_LISTENING;
+ break;
+ case BR_STATE_LEARNING:
+ stp_state = MT7530_STP_LEARNING;
+ break;
+ case BR_STATE_FORWARDING:
+ default:
+ stp_state = MT7530_STP_FORWARDING;
+ break;
+ }
+
+ mt7530_rmw(priv, MT7530_SSP_P(port), FID_PST_MASK, stp_state);
+}
+
+static int
+mt7530_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+{
+ struct mt7530_priv *priv = ds->priv;
+ u32 port_bitmap = BIT(MT7530_CPU_PORT);
+ int i;
+
+ mutex_lock(&priv->reg_mutex);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ /* Add this port to the port matrix of the other ports in the
+ * same bridge. If the port is disabled, port matrix is kept
+ * and not being setup until the port becomes enabled.
+ */
+ if (ds->enabled_port_mask & BIT(i) && i != port) {
+ if (ds->ports[i].bridge_dev != bridge)
+ continue;
+ if (priv->ports[i].enable)
+ mt7530_set(priv, MT7530_PCR_P(i),
+ PCR_MATRIX(BIT(port)));
+ priv->ports[i].pm |= PCR_MATRIX(BIT(port));
+
+ port_bitmap |= BIT(i);
+ }
+ }
+
+ /* Add the all other ports to this port matrix. */
+ if (priv->ports[port].enable)
+ mt7530_rmw(priv, MT7530_PCR_P(port),
+ PCR_MATRIX_MASK, PCR_MATRIX(port_bitmap));
+ priv->ports[port].pm |= PCR_MATRIX(port_bitmap);
+
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static void
+mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int i;
+
+ mutex_lock(&priv->reg_mutex);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ /* Remove this port from the port matrix of the other ports
+ * in the same bridge. If the port is disabled, port matrix
+ * is kept and not being setup until the port becomes enabled.
+ */
+ if (ds->enabled_port_mask & BIT(i) && i != port) {
+ if (ds->ports[i].bridge_dev != bridge)
+ continue;
+ if (priv->ports[i].enable)
+ mt7530_clear(priv, MT7530_PCR_P(i),
+ PCR_MATRIX(BIT(port)));
+ priv->ports[i].pm &= ~PCR_MATRIX(BIT(port));
+ }
+ }
+
+ /* Set the cpu port to be the only one in the port matrix of
+ * this port.
+ */
+ if (priv->ports[port].enable)
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+ PCR_MATRIX(BIT(MT7530_CPU_PORT)));
+ priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT));
+
+ mutex_unlock(&priv->reg_mutex);
+}
+
+static int
+mt7530_port_fdb_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int ret;
+
+ /* Because auto-learned entrie shares the same FDB table.
+ * an entry is reserved with no port_mask to make sure fdb_add
+ * is called while the entry is still available.
+ */
+ mutex_lock(&priv->reg_mutex);
+ mt7530_fdb_write(priv, fdb->vid, 0, fdb->addr, -1, STATIC_ENT);
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
+ mutex_unlock(&priv->reg_mutex);
+
+ return ret;
+}
+
+static void
+mt7530_port_fdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
+{
+ struct mt7530_priv *priv = ds->priv;
+ u8 port_mask = BIT(port);
+
+ mutex_lock(&priv->reg_mutex);
+ mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_ENT);
+ mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
+ mutex_unlock(&priv->reg_mutex);
+}
+
+static int
+mt7530_port_fdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int ret;
+ u8 port_mask = BIT(port);
+
+ mutex_lock(&priv->reg_mutex);
+ mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_EMP);
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
+ mutex_unlock(&priv->reg_mutex);
+
+ return ret;
+}
+
+static int
+mt7530_port_fdb_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_fdb *fdb,
+ int (*cb)(struct switchdev_obj *obj))
+{
+ struct mt7530_priv *priv = ds->priv;
+ struct mt7530_fdb _fdb = { 0 };
+ int cnt = MT7530_NUM_FDB_RECORDS;
+ int ret = 0;
+ u32 rsp = 0;
+
+ mutex_lock(&priv->reg_mutex);
+
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_START, &rsp);
+ if (ret < 0)
+ goto err;
+
+ do {
+ if (rsp & ATC_SRCH_HIT) {
+ mt7530_fdb_read(priv, &_fdb);
+ if (_fdb.port_mask & BIT(port)) {
+ ether_addr_copy(fdb->addr, _fdb.mac);
+ fdb->vid = _fdb.vid;
+ fdb->ndm_state = _fdb.noarp ?
+ NUD_NOARP : NUD_REACHABLE;
+ ret = cb(&fdb->obj);
+ if (ret < 0)
+ break;
+ }
+ }
+ } while (--cnt &&
+ !(rsp & ATC_SRCH_END) &&
+ !mt7530_fdb_cmd(priv, MT7530_FDB_NEXT, &rsp));
+err:
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static enum dsa_tag_protocol
+mtk_get_tag_protocol(struct dsa_switch *ds)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ if (!dsa_is_cpu_port(ds, MT7530_CPU_PORT)) {
+ dev_warn(priv->dev,
+ "port not matched with tagging CPU port\n");
+ return DSA_TAG_PROTO_NONE;
+ } else {
+ return DSA_TAG_PROTO_MTK;
+ }
+}
+
+static int
+mt7530_setup(struct dsa_switch *ds)
+{
+ struct mt7530_priv *priv = ds->priv;
+ int ret, i;
+ u32 id, val;
+ struct device_node *dn;
+ struct mt7530_dummy_poll p;
+
+ /* The parent node of master_netdev which holds the common system
+ * controller also is the container for two GMACs nodes representing
+ * as two netdev instances.
+ */
+ dn = ds->master_netdev->dev.of_node->parent;
+ priv->ethernet = syscon_node_to_regmap(dn);
+ if (IS_ERR(priv->ethernet))
+ return PTR_ERR(priv->ethernet);
+
+ regulator_set_voltage(priv->core_pwr, 1000000, 1000000);
+ ret = regulator_enable(priv->core_pwr);
+ if (ret < 0) {
+ dev_err(priv->dev,
+ "Failed to enable core power: %d\n", ret);
+ return ret;
+ }
+
+ regulator_set_voltage(priv->io_pwr, 3300000, 3300000);
+ ret = regulator_enable(priv->io_pwr);
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to enable io pwr: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* Reset whole chip through gpio pin or memory-mapped registers for
+ * different type of hardware
+ */
+ if (priv->mcm) {
+ reset_control_assert(priv->rstc);
+ usleep_range(1000, 1100);
+ reset_control_deassert(priv->rstc);
+ } else {
+ gpiod_set_value_cansleep(priv->reset, 0);
+ usleep_range(1000, 1100);
+ gpiod_set_value_cansleep(priv->reset, 1);
+ }
+
+ /* Waiting for MT7530 got to stable */
+ INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP);
+ ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0,
+ 20, 1000000);
+ if (ret < 0) {
+ dev_err(priv->dev, "reset timeout\n");
+ return ret;
+ }
+
+ id = mt7530_read(priv, MT7530_CREV);
+ id >>= CHIP_NAME_SHIFT;
+ if (id != MT7530_ID) {
+ dev_err(priv->dev, "chip %x can't be supported\n", id);
+ return -ENODEV;
+ }
+
+ /* Reset the switch through internal reset */
+ mt7530_write(priv, MT7530_SYS_CTRL,
+ SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
+ SYS_CTRL_REG_RST);
+
+ /* Enable Port 6 only; P5 as GMAC5 which currently is not supported */
+ val = mt7530_read(priv, MT7530_MHWTRAP);
+ val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS;
+ val |= MHWTRAP_MANUAL;
+ mt7530_write(priv, MT7530_MHWTRAP, val);
+
+ /* Enable and reset MIB counters */
+ mt7530_mib_reset(ds);
+
+ mt7530_clear(priv, MT7530_MFC, UNU_FFP_MASK);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ /* Disable forwarding by default on all ports */
+ mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
+ PCR_MATRIX_CLR);
+
+ if (dsa_is_cpu_port(ds, i))
+ mt7530_cpu_port_enable(priv, i);
+ else
+ mt7530_port_disable(ds, i, NULL);
+ }
+
+ /* Flush the FDB table */
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct dsa_switch_ops mt7530_switch_ops = {
+ .get_tag_protocol = mtk_get_tag_protocol,
+ .setup = mt7530_setup,
+ .get_strings = mt7530_get_strings,
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .get_ethtool_stats = mt7530_get_ethtool_stats,
+ .get_sset_count = mt7530_get_sset_count,
+ .adjust_link = mt7530_adjust_link,
+ .port_enable = mt7530_port_enable,
+ .port_disable = mt7530_port_disable,
+ .port_stp_state_set = mt7530_stp_state_set,
+ .port_bridge_join = mt7530_port_bridge_join,
+ .port_bridge_leave = mt7530_port_bridge_leave,
+ .port_fdb_prepare = mt7530_port_fdb_prepare,
+ .port_fdb_add = mt7530_port_fdb_add,
+ .port_fdb_del = mt7530_port_fdb_del,
+ .port_fdb_dump = mt7530_port_fdb_dump,
+};
+
+static int
+mt7530_probe(struct mdio_device *mdiodev)
+{
+ struct mt7530_priv *priv;
+ struct device_node *dn;
+
+ dn = mdiodev->dev.of_node;
+
+ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS);
+ if (!priv->ds)
+ return -ENOMEM;
+
+ /* Use medatek,mcm property to distinguish hardware type that would
+ * casues a little bit differences on power-on sequence.
+ */
+ priv->mcm = of_property_read_bool(dn, "mediatek,mcm");
+ if (priv->mcm) {
+ dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n");
+
+ priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm");
+ if (IS_ERR(priv->rstc)) {
+ dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
+ return PTR_ERR(priv->rstc);
+ }
+ }
+
+ priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
+ if (IS_ERR(priv->core_pwr))
+ return PTR_ERR(priv->core_pwr);
+
+ priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
+ if (IS_ERR(priv->io_pwr))
+ return PTR_ERR(priv->io_pwr);
+
+ /* Not MCM that indicates switch works as the remote standalone
+ * integrated circuit so the GPIO pin would be used to complete
+ * the reset, otherwise memory-mapped register accessing used
+ * through syscon provides in the case of MCM.
+ */
+ if (!priv->mcm) {
+ priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset)) {
+ dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
+ return PTR_ERR(priv->reset);
+ }
+ }
+
+ priv->bus = mdiodev->bus;
+ priv->dev = &mdiodev->dev;
+ priv->ds->priv = priv;
+ priv->ds->ops = &mt7530_switch_ops;
+ mutex_init(&priv->reg_mutex);
+ dev_set_drvdata(&mdiodev->dev, priv);
+
+ return dsa_register_switch(priv->ds, &mdiodev->dev);
+}
+
+static void
+mt7530_remove(struct mdio_device *mdiodev)
+{
+ struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
+ int ret = 0;
+
+ ret = regulator_disable(priv->core_pwr);
+ if (ret < 0)
+ dev_err(priv->dev,
+ "Failed to disable core power: %d\n", ret);
+
+ ret = regulator_disable(priv->io_pwr);
+ if (ret < 0)
+ dev_err(priv->dev, "Failed to disable io pwr: %d\n",
+ ret);
+
+ dsa_unregister_switch(priv->ds);
+ mutex_destroy(&priv->reg_mutex);
+}
+
+static const struct of_device_id mt7530_of_match[] = {
+ { .compatible = "mediatek,mt7530" },
+ { /* sentinel */ },
+};
+
+static struct mdio_driver mt7530_mdio_driver = {
+ .probe = mt7530_probe,
+ .remove = mt7530_remove,
+ .mdiodrv.driver = {
+ .name = "mt7530",
+ .of_match_table = mt7530_of_match,
+ },
+};
+
+mdio_module_driver(mt7530_mdio_driver);
+
+MODULE_AUTHOR("Sean Wang <[email protected]>");
+MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mediatek-mt7530");
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
new file mode 100644
index 000000000000..b83d76b99802
--- /dev/null
+++ b/drivers/net/dsa/mt7530.h
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2017 Sean Wang <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MT7530_H
+#define __MT7530_H
+
+#define MT7530_NUM_PORTS 7
+#define MT7530_CPU_PORT 6
+#define MT7530_NUM_FDB_RECORDS 2048
+
+#define NUM_TRGMII_CTRL 5
+
+#define TRGMII_BASE(x) (0x10000 + (x))
+
+/* Registers to ethsys access */
+#define ETHSYS_CLKCFG0 0x2c
+#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
+
+#define SYSC_REG_RSTCTRL 0x34
+#define RESET_MCM BIT(2)
+
+/* Registers to mac forward control for unknown frames */
+#define MT7530_MFC 0x10
+#define BC_FFP(x) (((x) & 0xff) << 24)
+#define UNM_FFP(x) (((x) & 0xff) << 16)
+#define UNU_FFP(x) (((x) & 0xff) << 8)
+#define UNU_FFP_MASK UNU_FFP(~0)
+
+/* Registers for address table access */
+#define MT7530_ATA1 0x74
+#define STATIC_EMP 0
+#define STATIC_ENT 3
+#define MT7530_ATA2 0x78
+
+/* Register for address table write data */
+#define MT7530_ATWD 0x7c
+
+/* Register for address table control */
+#define MT7530_ATC 0x80
+#define ATC_HASH (((x) & 0xfff) << 16)
+#define ATC_BUSY BIT(15)
+#define ATC_SRCH_END BIT(14)
+#define ATC_SRCH_HIT BIT(13)
+#define ATC_INVALID BIT(12)
+#define ATC_MAT(x) (((x) & 0xf) << 8)
+#define ATC_MAT_MACTAB ATC_MAT(0)
+
+enum mt7530_fdb_cmd {
+ MT7530_FDB_READ = 0,
+ MT7530_FDB_WRITE = 1,
+ MT7530_FDB_FLUSH = 2,
+ MT7530_FDB_START = 4,
+ MT7530_FDB_NEXT = 5,
+};
+
+/* Registers for table search read address */
+#define MT7530_TSRA1 0x84
+#define MAC_BYTE_0 24
+#define MAC_BYTE_1 16
+#define MAC_BYTE_2 8
+#define MAC_BYTE_3 0
+#define MAC_BYTE_MASK 0xff
+
+#define MT7530_TSRA2 0x88
+#define MAC_BYTE_4 24
+#define MAC_BYTE_5 16
+#define CVID 0
+#define CVID_MASK 0xfff
+
+#define MT7530_ATRD 0x8C
+#define AGE_TIMER 24
+#define AGE_TIMER_MASK 0xff
+#define PORT_MAP 4
+#define PORT_MAP_MASK 0xff
+#define ENT_STATUS 2
+#define ENT_STATUS_MASK 0x3
+
+/* Register for vlan table control */
+#define MT7530_VTCR 0x90
+#define VTCR_BUSY BIT(31)
+#define VTCR_FUNC (((x) & 0xf) << 12)
+#define VTCR_FUNC_RD_VID 0x1
+#define VTCR_FUNC_WR_VID 0x2
+#define VTCR_FUNC_INV_VID 0x3
+#define VTCR_FUNC_VAL_VID 0x4
+#define VTCR_VID ((x) & 0xfff)
+
+/* Register for setup vlan and acl write data */
+#define MT7530_VAWD1 0x94
+#define PORT_STAG BIT(31)
+#define IVL_MAC BIT(30)
+#define PORT_MEM(x) (((x) & 0xff) << 16)
+#define VALID BIT(1)
+
+#define MT7530_VAWD2 0x98
+
+/* Register for port STP state control */
+#define MT7530_SSP_P(x) (0x2000 + ((x) * 0x100))
+#define FID_PST(x) ((x) & 0x3)
+#define FID_PST_MASK FID_PST(0x3)
+
+enum mt7530_stp_state {
+ MT7530_STP_DISABLED = 0,
+ MT7530_STP_BLOCKING = 1,
+ MT7530_STP_LISTENING = 1,
+ MT7530_STP_LEARNING = 2,
+ MT7530_STP_FORWARDING = 3
+};
+
+/* Register for port control */
+#define MT7530_PCR_P(x) (0x2004 + ((x) * 0x100))
+#define PORT_VLAN(x) ((x) & 0x3)
+#define PCR_MATRIX(x) (((x) & 0xff) << 16)
+#define PORT_PRI(x) (((x) & 0x7) << 24)
+#define EG_TAG(x) (((x) & 0x3) << 28)
+#define PCR_MATRIX_MASK PCR_MATRIX(0xff)
+#define PCR_MATRIX_CLR PCR_MATRIX(0)
+
+/* Register for port security control */
+#define MT7530_PSC_P(x) (0x200c + ((x) * 0x100))
+#define SA_DIS BIT(4)
+
+/* Register for port vlan control */
+#define MT7530_PVC_P(x) (0x2010 + ((x) * 0x100))
+#define PORT_SPEC_TAG BIT(5)
+#define VLAN_ATTR(x) (((x) & 0x3) << 6)
+#define STAG_VPID (((x) & 0xffff) << 16)
+
+/* Register for port port-and-protocol based vlan 1 control */
+#define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100))
+
+/* Register for port MAC control register */
+#define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100))
+#define PMCR_IFG_XMIT(x) (((x) & 0x3) << 18)
+#define PMCR_MAC_MODE BIT(16)
+#define PMCR_FORCE_MODE BIT(15)
+#define PMCR_TX_EN BIT(14)
+#define PMCR_RX_EN BIT(13)
+#define PMCR_BACKOFF_EN BIT(9)
+#define PMCR_BACKPR_EN BIT(8)
+#define PMCR_TX_FC_EN BIT(5)
+#define PMCR_RX_FC_EN BIT(4)
+#define PMCR_FORCE_SPEED_1000 BIT(3)
+#define PMCR_FORCE_FDX BIT(1)
+#define PMCR_FORCE_LNK BIT(0)
+#define PMCR_COMMON_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
+ PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \
+ PMCR_TX_EN | PMCR_RX_EN | \
+ PMCR_TX_FC_EN | PMCR_RX_FC_EN)
+#define PMCR_CPUP_LINK (PMCR_COMMON_LINK | PMCR_FORCE_MODE | \
+ PMCR_FORCE_SPEED_1000 | \
+ PMCR_FORCE_FDX | \
+ PMCR_FORCE_LNK)
+#define PMCR_USERP_LINK PMCR_COMMON_LINK
+#define PMCR_FIXED_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
+ PMCR_FORCE_MODE | PMCR_TX_EN | \
+ PMCR_RX_EN | PMCR_BACKPR_EN | \
+ PMCR_BACKOFF_EN | \
+ PMCR_FORCE_SPEED_1000 | \
+ PMCR_FORCE_FDX | \
+ PMCR_FORCE_LNK)
+#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \
+ PMCR_TX_FC_EN | PMCR_RX_FC_EN)
+
+#define MT7530_PMSR_P(x) (0x3008 + (x) * 0x100)
+
+/* Register for MIB */
+#define MT7530_PORT_MIB_COUNTER(x) (0x4000 + (x) * 0x100)
+#define MT7530_MIB_CCR 0x4fe0
+#define CCR_MIB_ENABLE BIT(31)
+#define CCR_RX_OCT_CNT_GOOD BIT(7)
+#define CCR_RX_OCT_CNT_BAD BIT(6)
+#define CCR_TX_OCT_CNT_GOOD BIT(5)
+#define CCR_TX_OCT_CNT_BAD BIT(4)
+#define CCR_MIB_FLUSH (CCR_RX_OCT_CNT_GOOD | \
+ CCR_RX_OCT_CNT_BAD | \
+ CCR_TX_OCT_CNT_GOOD | \
+ CCR_TX_OCT_CNT_BAD)
+#define CCR_MIB_ACTIVATE (CCR_MIB_ENABLE | \
+ CCR_RX_OCT_CNT_GOOD | \
+ CCR_RX_OCT_CNT_BAD | \
+ CCR_TX_OCT_CNT_GOOD | \
+ CCR_TX_OCT_CNT_BAD)
+/* Register for system reset */
+#define MT7530_SYS_CTRL 0x7000
+#define SYS_CTRL_PHY_RST BIT(2)
+#define SYS_CTRL_SW_RST BIT(1)
+#define SYS_CTRL_REG_RST BIT(0)
+
+/* Register for hw trap status */
+#define MT7530_HWTRAP 0x7800
+
+/* Register for hw trap modification */
+#define MT7530_MHWTRAP 0x7804
+#define MHWTRAP_MANUAL BIT(16)
+#define MHWTRAP_P5_MAC_SEL BIT(13)
+#define MHWTRAP_P6_DIS BIT(8)
+#define MHWTRAP_P5_RGMII_MODE BIT(7)
+#define MHWTRAP_P5_DIS BIT(6)
+#define MHWTRAP_PHY_ACCESS BIT(5)
+
+/* Register for TOP signal control */
+#define MT7530_TOP_SIG_CTRL 0x7808
+#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16))
+
+#define MT7530_IO_DRV_CR 0x7810
+#define P5_IO_CLK_DRV(x) ((x) & 0x3)
+#define P5_IO_DATA_DRV(x) (((x) & 0x3) << 4)
+
+#define MT7530_P6ECR 0x7830
+#define P6_INTF_MODE_MASK 0x3
+#define P6_INTF_MODE(x) ((x) & 0x3)
+
+/* Registers for TRGMII on the both side */
+#define MT7530_TRGMII_RCK_CTRL 0x7a00
+#define GSW_TRGMII_RCK_CTRL 0x300
+#define RX_RST BIT(31)
+#define RXC_DQSISEL BIT(30)
+#define DQSI1_TAP_MASK (0x7f << 8)
+#define DQSI0_TAP_MASK 0x7f
+#define DQSI1_TAP(x) (((x) & 0x7f) << 8)
+#define DQSI0_TAP(x) ((x) & 0x7f)
+
+#define MT7530_TRGMII_RCK_RTT 0x7a04
+#define GSW_TRGMII_RCK_RTT 0x304
+#define DQS1_GATE BIT(31)
+#define DQS0_GATE BIT(30)
+
+#define MT7530_TRGMII_RD(x) (0x7a10 + (x) * 8)
+#define GSW_TRGMII_RD(x) (0x310 + (x) * 8)
+#define BSLIP_EN BIT(31)
+#define EDGE_CHK BIT(30)
+#define RD_TAP_MASK 0x7f
+#define RD_TAP(x) ((x) & 0x7f)
+
+#define GSW_TRGMII_TXCTRL 0x340
+#define MT7530_TRGMII_TXCTRL 0x7a40
+#define TRAIN_TXEN BIT(31)
+#define TXC_INV BIT(30)
+#define TX_RST BIT(28)
+
+#define MT7530_TRGMII_TD_ODT(i) (0x7a54 + 8 * (i))
+#define GSW_TRGMII_TD_ODT(i) (0x354 + 8 * (i))
+#define TD_DM_DRVP(x) ((x) & 0xf)
+#define TD_DM_DRVN(x) (((x) & 0xf) << 4)
+
+#define GSW_INTF_MODE 0x390
+#define INTF_MODE_TRGMII BIT(1)
+
+#define MT7530_TRGMII_TCK_CTRL 0x7a78
+#define TCK_TAP(x) (((x) & 0xf) << 8)
+
+#define MT7530_P5RGMIIRXCR 0x7b00
+#define CSR_RGMII_EDGE_ALIGN BIT(8)
+#define CSR_RGMII_RXC_0DEG_CFG(x) ((x) & 0xf)
+
+#define MT7530_P5RGMIITXCR 0x7b04
+#define CSR_RGMII_TXC_CFG(x) ((x) & 0x1f)
+
+#define MT7530_CREV 0x7ffc
+#define CHIP_NAME_SHIFT 16
+#define MT7530_ID 0x7530
+
+/* Registers for core PLL access through mmd indirect */
+#define CORE_PLL_GROUP2 0x401
+#define RG_SYSPLL_EN_NORMAL BIT(15)
+#define RG_SYSPLL_VODEN BIT(14)
+#define RG_SYSPLL_LF BIT(13)
+#define RG_SYSPLL_RST_DLY(x) (((x) & 0x3) << 12)
+#define RG_SYSPLL_LVROD_EN BIT(10)
+#define RG_SYSPLL_PREDIV(x) (((x) & 0x3) << 8)
+#define RG_SYSPLL_POSDIV(x) (((x) & 0x3) << 5)
+#define RG_SYSPLL_FBKSEL BIT(4)
+#define RT_SYSPLL_EN_AFE_OLT BIT(0)
+
+#define CORE_PLL_GROUP4 0x403
+#define RG_SYSPLL_DDSFBK_EN BIT(12)
+#define RG_SYSPLL_BIAS_EN BIT(11)
+#define RG_SYSPLL_BIAS_LPF_EN BIT(10)
+
+#define CORE_PLL_GROUP5 0x404
+#define RG_LCDDS_PCW_NCPO1(x) ((x) & 0xffff)
+
+#define CORE_PLL_GROUP6 0x405
+#define RG_LCDDS_PCW_NCPO0(x) ((x) & 0xffff)
+
+#define CORE_PLL_GROUP7 0x406
+#define RG_LCDDS_PWDB BIT(15)
+#define RG_LCDDS_ISO_EN BIT(13)
+#define RG_LCCDS_C(x) (((x) & 0x7) << 4)
+#define RG_LCDDS_PCW_NCPO_CHG BIT(3)
+
+#define CORE_PLL_GROUP10 0x409
+#define RG_LCDDS_SSC_DELTA(x) ((x) & 0xfff)
+
+#define CORE_PLL_GROUP11 0x40a
+#define RG_LCDDS_SSC_DELTA1(x) ((x) & 0xfff)
+
+#define CORE_GSWPLL_GRP1 0x40d
+#define RG_GSWPLL_PREDIV(x) (((x) & 0x3) << 14)
+#define RG_GSWPLL_POSDIV_200M(x) (((x) & 0x3) << 12)
+#define RG_GSWPLL_EN_PRE BIT(11)
+#define RG_GSWPLL_FBKSEL BIT(10)
+#define RG_GSWPLL_BP BIT(9)
+#define RG_GSWPLL_BR BIT(8)
+#define RG_GSWPLL_FBKDIV_200M(x) ((x) & 0xff)
+
+#define CORE_GSWPLL_GRP2 0x40e
+#define RG_GSWPLL_POSDIV_500M(x) (((x) & 0x3) << 8)
+#define RG_GSWPLL_FBKDIV_500M(x) ((x) & 0xff)
+
+#define CORE_TRGMII_GSW_CLK_CG 0x410
+#define REG_GSWCK_EN BIT(0)
+#define REG_TRGMIICK_EN BIT(1)
+
+#define MIB_DESC(_s, _o, _n) \
+ { \
+ .size = (_s), \
+ .offset = (_o), \
+ .name = (_n), \
+ }
+
+struct mt7530_mib_desc {
+ unsigned int size;
+ unsigned int offset;
+ const char *name;
+};
+
+struct mt7530_fdb {
+ u16 vid;
+ u8 port_mask;
+ u8 aging;
+ u8 mac[6];
+ bool noarp;
+};
+
+struct mt7530_port {
+ bool enable;
+ u32 pm;
+};
+
+/* struct mt7530_priv - This is the main data structure for holding the state
+ * of the driver
+ * @dev: The device pointer
+ * @ds: The pointer to the dsa core structure
+ * @bus: The bus used for the device and built-in PHY
+ * @rstc: The pointer to reset control used by MCM
+ * @ethernet: The regmap used for access TRGMII-based registers
+ * @core_pwr: The power supplied into the core
+ * @io_pwr: The power supplied into the I/O
+ * @reset: The descriptor for GPIO line tied to its reset pin
+ * @mcm: Flag for distinguishing if standalone IC or module
+ * coupling
+ * @ports: Holding the state among ports
+ * @reg_mutex: The lock for protecting among process accessing
+ * registers
+ */
+struct mt7530_priv {
+ struct device *dev;
+ struct dsa_switch *ds;
+ struct mii_bus *bus;
+ struct reset_control *rstc;
+ struct regmap *ethernet;
+ struct regulator *core_pwr;
+ struct regulator *io_pwr;
+ struct gpio_desc *reset;
+ bool mcm;
+
+ struct mt7530_port ports[MT7530_NUM_PORTS];
+ /* protect among processes for registers access*/
+ struct mutex reg_mutex;
+};
+
+struct mt7530_hw_stats {
+ const char *string;
+ u16 reg;
+ u8 sizeof_stat;
+};
+
+struct mt7530_dummy_poll {
+ struct mt7530_priv *priv;
+ u32 reg;
+};
+
+static inline void INIT_MT7530_DUMMY_POLL(struct mt7530_dummy_poll *p,
+ struct mt7530_priv *priv, u32 reg)
+{
+ p->priv = priv;
+ p->reg = reg;
+}
+
+#endif /* __MT7530_H */
diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c
index 084a6d58543a..be823c186517 100644
--- a/drivers/net/ethernet/3com/typhoon.c
+++ b/drivers/net/ethernet/3com/typhoon.c
@@ -283,7 +283,6 @@ struct typhoon {
spinlock_t command_lock ____cacheline_aligned;
struct basic_ring cmdRing;
struct basic_ring respRing;
- struct net_device_stats stats;
struct net_device_stats stats_saved;
struct typhoon_shared * shared;
dma_addr_t shared_dma;
@@ -898,7 +897,7 @@ typhoon_set_rx_mode(struct net_device *dev)
static int
typhoon_do_get_stats(struct typhoon *tp)
{
- struct net_device_stats *stats = &tp->stats;
+ struct net_device_stats *stats = &tp->dev->stats;
struct net_device_stats *saved = &tp->stats_saved;
struct cmd_desc xp_cmd;
struct resp_desc xp_resp[7];
@@ -951,7 +950,7 @@ static struct net_device_stats *
typhoon_get_stats(struct net_device *dev)
{
struct typhoon *tp = netdev_priv(dev);
- struct net_device_stats *stats = &tp->stats;
+ struct net_device_stats *stats = &tp->dev->stats;
struct net_device_stats *saved = &tp->stats_saved;
smp_rmb();
@@ -1991,7 +1990,7 @@ typhoon_stop_runtime(struct typhoon *tp, int wait_type)
tp->card_state = Sleeping;
smp_wmb();
typhoon_do_get_stats(tp);
- memcpy(&tp->stats_saved, &tp->stats, sizeof(struct net_device_stats));
+ memcpy(&tp->stats_saved, &tp->dev->stats, sizeof(struct net_device_stats));
INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_HALT);
typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c
index b556c926557a..9c152d85840d 100644
--- a/drivers/net/ethernet/amd/nmclan_cs.c
+++ b/drivers/net/ethernet/amd/nmclan_cs.c
@@ -359,7 +359,6 @@ typedef struct _mace_statistics {
typedef struct _mace_private {
struct pcmcia_device *p_dev;
- struct net_device_stats linux_stats; /* Linux statistics counters */
mace_statistics mace_stats; /* MACE chip statistics counters */
/* restore_multicast_list() state variables */
@@ -879,7 +878,7 @@ static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
service a transmit interrupt while we are in here.
*/
- lp->linux_stats.tx_bytes += skb->len;
+ dev->stats.tx_bytes += skb->len;
lp->tx_free_frames--;
/* WARNING: Write the _exact_ number of bytes written in the header! */
@@ -967,7 +966,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id)
fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC);
if ((fifofc & MACE_FIFOFC_XMTFC)==0) {
- lp->linux_stats.tx_errors++;
+ dev->stats.tx_errors++;
outb(0xFF, ioaddr + AM2150_XMT_SKIP);
}
@@ -1016,7 +1015,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id)
} /* if (xmtfs & MACE_XMTFS_XMTSV) */
- lp->linux_stats.tx_packets++;
+ dev->stats.tx_packets++;
lp->tx_free_frames++;
netif_wake_queue(dev);
} /* if (status & MACE_IR_XMTINT) */
@@ -1077,7 +1076,7 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt)
" 0x%X.\n", dev->name, rx_framecnt, rx_status);
if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */
- lp->linux_stats.rx_errors++;
+ dev->stats.rx_errors++;
if (rx_status & MACE_RCVFS_OFLO) {
lp->mace_stats.oflo++;
}
@@ -1114,14 +1113,14 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt)
netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
- lp->linux_stats.rx_packets++;
- lp->linux_stats.rx_bytes += pkt_len;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
continue;
} else {
pr_debug("%s: couldn't allocate a sk_buff of size"
" %d.\n", dev->name, pkt_len);
- lp->linux_stats.rx_dropped++;
+ dev->stats.rx_dropped++;
}
}
outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
@@ -1231,13 +1230,13 @@ static void update_stats(unsigned int ioaddr, struct net_device *dev)
lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC);
lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC);
/* At this point, mace_stats is fully updated for this call.
- We may now update the linux_stats. */
+ We may now update the netdev stats. */
- /* The MACE has no equivalent for linux_stats field which are commented
+ /* The MACE has no equivalent for netdev stats field which are commented
out. */
- /* lp->linux_stats.multicast; */
- lp->linux_stats.collisions =
+ /* dev->stats.multicast; */
+ dev->stats.collisions =
lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc;
/* Collision: The MACE may retry sending a packet 15 times
before giving up. The retry count is in XMTRC.
@@ -1245,22 +1244,22 @@ static void update_stats(unsigned int ioaddr, struct net_device *dev)
If so, why doesn't the RCVCC record these collisions? */
/* detailed rx_errors: */
- lp->linux_stats.rx_length_errors =
+ dev->stats.rx_length_errors =
lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc;
- /* lp->linux_stats.rx_over_errors */
- lp->linux_stats.rx_crc_errors = lp->mace_stats.fcs;
- lp->linux_stats.rx_frame_errors = lp->mace_stats.fram;
- lp->linux_stats.rx_fifo_errors = lp->mace_stats.oflo;
- lp->linux_stats.rx_missed_errors =
+ /* dev->stats.rx_over_errors */
+ dev->stats.rx_crc_errors = lp->mace_stats.fcs;
+ dev->stats.rx_frame_errors = lp->mace_stats.fram;
+ dev->stats.rx_fifo_errors = lp->mace_stats.oflo;
+ dev->stats.rx_missed_errors =
lp->mace_stats.mpco * 256 + lp->mace_stats.mpc;
/* detailed tx_errors */
- lp->linux_stats.tx_aborted_errors = lp->mace_stats.rtry;
- lp->linux_stats.tx_carrier_errors = lp->mace_stats.lcar;
+ dev->stats.tx_aborted_errors = lp->mace_stats.rtry;
+ dev->stats.tx_carrier_errors = lp->mace_stats.lcar;
/* LCAR usually results from bad cabling. */
- lp->linux_stats.tx_fifo_errors = lp->mace_stats.uflo;
- lp->linux_stats.tx_heartbeat_errors = lp->mace_stats.cerr;
- /* lp->linux_stats.tx_window_errors; */
+ dev->stats.tx_fifo_errors = lp->mace_stats.uflo;
+ dev->stats.tx_heartbeat_errors = lp->mace_stats.cerr;
+ /* dev->stats.tx_window_errors; */
} /* update_stats */
/* ----------------------------------------------------------------------------
@@ -1274,10 +1273,10 @@ static struct net_device_stats *mace_get_stats(struct net_device *dev)
update_stats(dev->base_addr, dev);
pr_debug("%s: updating the statistics.\n", dev->name);
- pr_linux_stats(&lp->linux_stats);
+ pr_linux_stats(&dev->stats);
pr_mace_stats(&lp->mace_stats);
- return &lp->linux_stats;
+ return &dev->stats;
} /* net_device_stats */
/* ----------------------------------------------------------------------------
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 30606b11b128..5cbd1e7a926a 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -684,8 +684,8 @@ static void macb_tx_error_task(struct work_struct *work)
netdev_vdbg(bp->dev, "txerr skb %u (data %p) TX complete\n",
macb_tx_ring_wrap(bp, tail),
skb->data);
- bp->stats.tx_packets++;
- bp->stats.tx_bytes += skb->len;
+ bp->dev->stats.tx_packets++;
+ bp->dev->stats.tx_bytes += skb->len;
}
} else {
/* "Buffers exhausted mid-frame" errors may only happen
@@ -778,8 +778,8 @@ static void macb_tx_interrupt(struct macb_queue *queue)
netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n",
macb_tx_ring_wrap(bp, tail),
skb->data);
- bp->stats.tx_packets++;
- bp->stats.tx_bytes += skb->len;
+ bp->dev->stats.tx_packets++;
+ bp->dev->stats.tx_bytes += skb->len;
}
/* Now we can safely release resources */
@@ -911,14 +911,14 @@ static int gem_rx(struct macb *bp, int budget)
if (!(ctrl & MACB_BIT(RX_SOF) && ctrl & MACB_BIT(RX_EOF))) {
netdev_err(bp->dev,
"not whole frame pointed by descriptor\n");
- bp->stats.rx_dropped++;
+ bp->dev->stats.rx_dropped++;
break;
}
skb = bp->rx_skbuff[entry];
if (unlikely(!skb)) {
netdev_err(bp->dev,
"inconsistent Rx descriptor chain\n");
- bp->stats.rx_dropped++;
+ bp->dev->stats.rx_dropped++;
break;
}
/* now everything is ready for receiving packet */
@@ -938,8 +938,8 @@ static int gem_rx(struct macb *bp, int budget)
GEM_BFEXT(RX_CSUM, ctrl) & GEM_RX_CSUM_CHECKED_MASK)
skb->ip_summed = CHECKSUM_UNNECESSARY;
- bp->stats.rx_packets++;
- bp->stats.rx_bytes += skb->len;
+ bp->dev->stats.rx_packets++;
+ bp->dev->stats.rx_bytes += skb->len;
#if defined(DEBUG) && defined(VERBOSE_DEBUG)
netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n",
@@ -984,7 +984,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
*/
skb = netdev_alloc_skb(bp->dev, len + NET_IP_ALIGN);
if (!skb) {
- bp->stats.rx_dropped++;
+ bp->dev->stats.rx_dropped++;
for (frag = first_frag; ; frag++) {
desc = macb_rx_desc(bp, frag);
desc->addr &= ~MACB_BIT(RX_USED);
@@ -1030,8 +1030,8 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
__skb_pull(skb, NET_IP_ALIGN);
skb->protocol = eth_type_trans(skb, bp->dev);
- bp->stats.rx_packets++;
- bp->stats.rx_bytes += skb->len;
+ bp->dev->stats.rx_packets++;
+ bp->dev->stats.rx_bytes += skb->len;
netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n",
skb->len, skb->csum);
netif_receive_skb(skb);
@@ -2210,7 +2210,7 @@ static void gem_update_stats(struct macb *bp)
static struct net_device_stats *gem_get_stats(struct macb *bp)
{
struct gem_stats *hwstat = &bp->hw_stats.gem;
- struct net_device_stats *nstat = &bp->stats;
+ struct net_device_stats *nstat = &bp->dev->stats;
gem_update_stats(bp);
@@ -2281,7 +2281,7 @@ static void gem_get_ethtool_strings(struct net_device *dev, u32 sset, u8 *p)
static struct net_device_stats *macb_get_stats(struct net_device *dev)
{
struct macb *bp = netdev_priv(dev);
- struct net_device_stats *nstat = &bp->stats;
+ struct net_device_stats *nstat = &bp->dev->stats;
struct macb_stats *hwstat = &bp->hw_stats.macb;
if (macb_is_gem(bp))
@@ -2993,15 +2993,15 @@ static void at91ether_rx(struct net_device *dev)
memcpy(skb_put(skb, pktlen), p_recv, pktlen);
skb->protocol = eth_type_trans(skb, dev);
- lp->stats.rx_packets++;
- lp->stats.rx_bytes += pktlen;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pktlen;
netif_rx(skb);
} else {
- lp->stats.rx_dropped++;
+ dev->stats.rx_dropped++;
}
if (desc->ctrl & MACB_BIT(RX_MHASH_MATCH))
- lp->stats.multicast++;
+ dev->stats.multicast++;
/* reset ownership bit */
desc->addr &= ~MACB_BIT(RX_USED);
@@ -3036,15 +3036,15 @@ static irqreturn_t at91ether_interrupt(int irq, void *dev_id)
if (intstatus & MACB_BIT(TCOMP)) {
/* The TCOM bit is set even if the transmission failed */
if (intstatus & (MACB_BIT(ISR_TUND) | MACB_BIT(ISR_RLE)))
- lp->stats.tx_errors++;
+ dev->stats.tx_errors++;
if (lp->skb) {
dev_kfree_skb_irq(lp->skb);
lp->skb = NULL;
dma_unmap_single(NULL, lp->skb_physaddr,
lp->skb_length, DMA_TO_DEVICE);
- lp->stats.tx_packets++;
- lp->stats.tx_bytes += lp->skb_length;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += lp->skb_length;
}
netif_wake_queue(dev);
}
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 234a49eaccfd..ec037b0fa2a4 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -919,7 +919,6 @@ struct macb {
struct clk *rx_clk;
struct net_device *dev;
struct napi_struct napi;
- struct net_device_stats stats;
union {
struct macb_stats macb;
struct gem_stats gem;
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 24017588f531..330d8a03ea11 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -1665,8 +1665,9 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto err_unregister_interrupts;
- netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG |
- NETIF_F_TSO | NETIF_F_GRO |
+ netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_SG |
+ NETIF_F_TSO | NETIF_F_GRO | NETIF_F_TSO6 |
+ NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_HW_VLAN_CTAG_RX);
netdev->hw_features |= NETIF_F_RXHASH;
@@ -1674,7 +1675,8 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
netdev->features |= netdev->hw_features;
netdev->hw_features |= NETIF_F_LOOPBACK;
- netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
+ netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6;
netdev->netdev_ops = &nicvf_netdev_ops;
netdev->watchdog_timeo = NICVF_TX_TIMEOUT;
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
index f13289f0d238..7b0fd8d871cc 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
@@ -1094,7 +1094,13 @@ nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry,
{
int proto;
struct sq_hdr_subdesc *hdr;
+ union {
+ struct iphdr *v4;
+ struct ipv6hdr *v6;
+ unsigned char *hdr;
+ } ip;
+ ip.hdr = skb_network_header(skb);
hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry);
memset(hdr, 0, SND_QUEUE_DESC_SIZE);
hdr->subdesc_type = SQ_DESC_TYPE_HEADER;
@@ -1119,7 +1125,9 @@ nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry,
hdr->l3_offset = skb_network_offset(skb);
hdr->l4_offset = skb_transport_offset(skb);
- proto = ip_hdr(skb)->protocol;
+ proto = (ip.v4->version == 4) ? ip.v4->protocol :
+ ip.v6->nexthdr;
+
switch (proto) {
case IPPROTO_TCP:
hdr->csum_l4 = SEND_L4_CSUM_TCP;
diff --git a/drivers/net/ethernet/chelsio/cxgb/common.h b/drivers/net/ethernet/chelsio/cxgb/common.h
index 6916c62f2487..94b9482f14a5 100644
--- a/drivers/net/ethernet/chelsio/cxgb/common.h
+++ b/drivers/net/ethernet/chelsio/cxgb/common.h
@@ -223,7 +223,6 @@ struct port_info {
struct cmac *mac;
struct cphy *phy;
struct link_config link_config;
- struct net_device_stats netstats;
};
struct sge;
diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c
index d8aff7a4b3c7..8623be13bf86 100644
--- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c
+++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c
@@ -296,7 +296,7 @@ static struct net_device_stats *t1_get_stats(struct net_device *dev)
{
struct adapter *adapter = dev->ml_priv;
struct port_info *p = &adapter->port[dev->if_port];
- struct net_device_stats *ns = &p->netstats;
+ struct net_device_stats *ns = &dev->stats;
const struct cmac_statistics *pstats;
/* Do a full update of the MAC stats */
diff --git a/drivers/net/ethernet/chelsio/cxgb3/adapter.h b/drivers/net/ethernet/chelsio/cxgb3/adapter.h
index 8b395b537330..087ff0ffb597 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/adapter.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/adapter.h
@@ -72,7 +72,6 @@ struct port_info {
struct cphy phy;
struct cmac mac;
struct link_config link_config;
- struct net_device_stats netstats;
int activity;
__be32 iscsi_ipv4addr;
struct iscsi_config iscsic;
diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
index d76491676b51..2ff6bd139c96 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
@@ -1489,7 +1489,7 @@ static struct net_device_stats *cxgb_get_stats(struct net_device *dev)
{
struct port_info *pi = netdev_priv(dev);
struct adapter *adapter = pi->adapter;
- struct net_device_stats *ns = &pi->netstats;
+ struct net_device_stats *ns = &dev->stats;
const struct mac_stats *pstats;
spin_lock(&adapter->stats_lock);
diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c
index 127ce9707378..91b8f6f5a765 100644
--- a/drivers/net/ethernet/dec/tulip/de2104x.c
+++ b/drivers/net/ethernet/dec/tulip/de2104x.c
@@ -312,8 +312,6 @@ struct de_private {
u32 msg_enable;
- struct net_device_stats net_stats;
-
struct pci_dev *pdev;
u16 setup_frame[DE_SETUP_FRAME_WORDS];
@@ -388,14 +386,14 @@ static void de_rx_err_acct (struct de_private *de, unsigned rx_tail,
netif_warn(de, rx_err, de->dev,
"Oversized Ethernet frame spanned multiple buffers, status %08x!\n",
status);
- de->net_stats.rx_length_errors++;
+ de->dev->stats.rx_length_errors++;
}
} else if (status & RxError) {
/* There was a fatal error. */
- de->net_stats.rx_errors++; /* end of a packet.*/
- if (status & 0x0890) de->net_stats.rx_length_errors++;
- if (status & RxErrCRC) de->net_stats.rx_crc_errors++;
- if (status & RxErrFIFO) de->net_stats.rx_fifo_errors++;
+ de->dev->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x0890) de->dev->stats.rx_length_errors++;
+ if (status & RxErrCRC) de->dev->stats.rx_crc_errors++;
+ if (status & RxErrFIFO) de->dev->stats.rx_fifo_errors++;
}
}
@@ -423,7 +421,7 @@ static void de_rx (struct de_private *de)
mapping = de->rx_skb[rx_tail].mapping;
if (unlikely(drop)) {
- de->net_stats.rx_dropped++;
+ de->dev->stats.rx_dropped++;
goto rx_next;
}
@@ -441,7 +439,7 @@ static void de_rx (struct de_private *de)
buflen = copying_skb ? (len + RX_OFFSET) : de->rx_buf_sz;
copy_skb = netdev_alloc_skb(de->dev, buflen);
if (unlikely(!copy_skb)) {
- de->net_stats.rx_dropped++;
+ de->dev->stats.rx_dropped++;
drop = 1;
rx_work = 100;
goto rx_next;
@@ -470,8 +468,8 @@ static void de_rx (struct de_private *de)
skb->protocol = eth_type_trans (skb, de->dev);
- de->net_stats.rx_packets++;
- de->net_stats.rx_bytes += skb->len;
+ de->dev->stats.rx_packets++;
+ de->dev->stats.rx_bytes += skb->len;
rc = netif_rx (skb);
if (rc == NET_RX_DROP)
drop = 1;
@@ -572,18 +570,18 @@ static void de_tx (struct de_private *de)
netif_dbg(de, tx_err, de->dev,
"tx err, status 0x%x\n",
status);
- de->net_stats.tx_errors++;
+ de->dev->stats.tx_errors++;
if (status & TxOWC)
- de->net_stats.tx_window_errors++;
+ de->dev->stats.tx_window_errors++;
if (status & TxMaxCol)
- de->net_stats.tx_aborted_errors++;
+ de->dev->stats.tx_aborted_errors++;
if (status & TxLinkFail)
- de->net_stats.tx_carrier_errors++;
+ de->dev->stats.tx_carrier_errors++;
if (status & TxFIFOUnder)
- de->net_stats.tx_fifo_errors++;
+ de->dev->stats.tx_fifo_errors++;
} else {
- de->net_stats.tx_packets++;
- de->net_stats.tx_bytes += skb->len;
+ de->dev->stats.tx_packets++;
+ de->dev->stats.tx_bytes += skb->len;
netif_dbg(de, tx_done, de->dev,
"tx done, slot %d\n", tx_tail);
}
@@ -814,9 +812,9 @@ static void de_set_rx_mode (struct net_device *dev)
static inline void de_rx_missed(struct de_private *de, u32 rx_missed)
{
if (unlikely(rx_missed & RxMissedOver))
- de->net_stats.rx_missed_errors += RxMissedMask;
+ de->dev->stats.rx_missed_errors += RxMissedMask;
else
- de->net_stats.rx_missed_errors += (rx_missed & RxMissedMask);
+ de->dev->stats.rx_missed_errors += (rx_missed & RxMissedMask);
}
static void __de_get_stats(struct de_private *de)
@@ -836,7 +834,7 @@ static struct net_device_stats *de_get_stats(struct net_device *dev)
__de_get_stats(de);
spin_unlock_irq(&de->lock);
- return &de->net_stats;
+ return &dev->stats;
}
static inline int de_is_running (struct de_private *de)
@@ -1348,7 +1346,7 @@ static void de_clean_rings (struct de_private *de)
struct sk_buff *skb = de->tx_skb[i].skb;
if ((skb) && (skb != DE_DUMMY_SKB)) {
if (skb != DE_SETUP_SKB) {
- de->net_stats.tx_dropped++;
+ de->dev->stats.tx_dropped++;
pci_unmap_single(de->pdev,
de->tx_skb[i].mapping,
skb->len, PCI_DMA_TODEVICE);
diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c
index 1e350135f11d..778f974e2928 100644
--- a/drivers/net/ethernet/dlink/dl2k.c
+++ b/drivers/net/ethernet/dlink/dl2k.c
@@ -878,10 +878,10 @@ tx_error (struct net_device *dev, int tx_status)
frame_id = (tx_status & 0xffff0000);
printk (KERN_ERR "%s: Transmit error, TxStatus %4.4x, FrameId %d.\n",
dev->name, tx_status, frame_id);
- np->stats.tx_errors++;
+ dev->stats.tx_errors++;
/* Ttransmit Underrun */
if (tx_status & 0x10) {
- np->stats.tx_fifo_errors++;
+ dev->stats.tx_fifo_errors++;
dw16(TxStartThresh, dr16(TxStartThresh) + 0x10);
/* Transmit Underrun need to set TxReset, DMARest, FIFOReset */
dw16(ASICCtrl + 2,
@@ -903,7 +903,7 @@ tx_error (struct net_device *dev, int tx_status)
}
/* Late Collision */
if (tx_status & 0x04) {
- np->stats.tx_fifo_errors++;
+ dev->stats.tx_fifo_errors++;
/* TxReset and clear FIFO */
dw16(ASICCtrl + 2, TxReset | FIFOReset);
/* Wait reset done */
@@ -916,13 +916,8 @@ tx_error (struct net_device *dev, int tx_status)
/* Let TxStartThresh stay default value */
}
/* Maximum Collisions */
-#ifdef ETHER_STATS
if (tx_status & 0x08)
- np->stats.collisions16++;
-#else
- if (tx_status & 0x08)
- np->stats.collisions++;
-#endif
+ dev->stats.collisions++;
/* Restart the Tx */
dw32(MACCtrl, dr16(MACCtrl) | TxEnable);
}
@@ -952,15 +947,15 @@ receive_packet (struct net_device *dev)
break;
/* Update rx error statistics, drop packet. */
if (frame_status & RFS_Errors) {
- np->stats.rx_errors++;
+ dev->stats.rx_errors++;
if (frame_status & (RxRuntFrame | RxLengthError))
- np->stats.rx_length_errors++;
+ dev->stats.rx_length_errors++;
if (frame_status & RxFCSError)
- np->stats.rx_crc_errors++;
+ dev->stats.rx_crc_errors++;
if (frame_status & RxAlignmentError && np->speed != 1000)
- np->stats.rx_frame_errors++;
+ dev->stats.rx_frame_errors++;
if (frame_status & RxFIFOOverrun)
- np->stats.rx_fifo_errors++;
+ dev->stats.rx_fifo_errors++;
} else {
struct sk_buff *skb;
@@ -1096,23 +1091,23 @@ get_stats (struct net_device *dev)
/* All statistics registers need to be acknowledged,
else statistic overflow could cause problems */
- np->stats.rx_packets += dr32(FramesRcvOk);
- np->stats.tx_packets += dr32(FramesXmtOk);
- np->stats.rx_bytes += dr32(OctetRcvOk);
- np->stats.tx_bytes += dr32(OctetXmtOk);
+ dev->stats.rx_packets += dr32(FramesRcvOk);
+ dev->stats.tx_packets += dr32(FramesXmtOk);
+ dev->stats.rx_bytes += dr32(OctetRcvOk);
+ dev->stats.tx_bytes += dr32(OctetXmtOk);
- np->stats.multicast = dr32(McstFramesRcvdOk);
- np->stats.collisions += dr32(SingleColFrames)
+ dev->stats.multicast = dr32(McstFramesRcvdOk);
+ dev->stats.collisions += dr32(SingleColFrames)
+ dr32(MultiColFrames);
/* detailed tx errors */
stat_reg = dr16(FramesAbortXSColls);
- np->stats.tx_aborted_errors += stat_reg;
- np->stats.tx_errors += stat_reg;
+ dev->stats.tx_aborted_errors += stat_reg;
+ dev->stats.tx_errors += stat_reg;
stat_reg = dr16(CarrierSenseErrors);
- np->stats.tx_carrier_errors += stat_reg;
- np->stats.tx_errors += stat_reg;
+ dev->stats.tx_carrier_errors += stat_reg;
+ dev->stats.tx_errors += stat_reg;
/* Clear all other statistic register. */
dr32(McstOctetXmtOk);
@@ -1142,7 +1137,7 @@ get_stats (struct net_device *dev)
dr16(TCPCheckSumErrors);
dr16(UDPCheckSumErrors);
dr16(IPCheckSumErrors);
- return &np->stats;
+ return &dev->stats;
}
static int
diff --git a/drivers/net/ethernet/dlink/dl2k.h b/drivers/net/ethernet/dlink/dl2k.h
index 5d8ae5320242..10e98ba33ebf 100644
--- a/drivers/net/ethernet/dlink/dl2k.h
+++ b/drivers/net/ethernet/dlink/dl2k.h
@@ -377,7 +377,6 @@ struct netdev_private {
void __iomem *eeprom_addr;
spinlock_t tx_lock;
spinlock_t rx_lock;
- struct net_device_stats stats;
unsigned int rx_buf_sz; /* Based on MTU+slack. */
unsigned int speed; /* Operating speed */
unsigned int vlan; /* VLAN Id */
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 20fc7c8d1a50..8447987577bd 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -43,8 +43,8 @@
#define RX_QUEUE_ENTRIES 256 /* must be power of 2 */
#define TX_QUEUE_ENTRIES 512 /* must be power of 2 */
-#define MAX_PKT_SIZE 1518
-#define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */
+#define MAX_PKT_SIZE 1536
+#define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */
struct ftgmac100_descs {
struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
@@ -60,7 +60,7 @@ struct ftgmac100 {
dma_addr_t descs_dma_addr;
/* Rx ring */
- struct page *rx_pages[RX_QUEUE_ENTRIES];
+ struct sk_buff *rx_skbs[RX_QUEUE_ENTRIES];
unsigned int rx_pointer;
u32 rxdes0_edorr_mask;
@@ -71,6 +71,10 @@ struct ftgmac100 {
u32 txdes0_edotr_mask;
spinlock_t tx_lock;
+ /* Scratch page to use when rx skb alloc fails */
+ void *rx_scratch;
+ dma_addr_t rx_scratch_dma;
+
/* Component structures */
struct net_device *netdev;
struct device *dev;
@@ -88,9 +92,6 @@ struct ftgmac100 {
bool need_mac_restart;
};
-static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes, gfp_t gfp);
-
static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr)
{
iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
@@ -277,128 +278,49 @@ static void ftgmac100_stop_hw(struct ftgmac100 *priv)
iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR);
}
-static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS);
-}
-
-static bool ftgmac100_rxdes_last_segment(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_LRS);
-}
-
-static bool ftgmac100_rxdes_packet_ready(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY);
-}
-
-static void ftgmac100_rxdes_set_dma_own(const struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes)
-{
- /* clear status bits */
- rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask);
-}
-
-static bool ftgmac100_rxdes_rx_error(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ERR);
-}
-
-static bool ftgmac100_rxdes_crc_error(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_CRC_ERR);
-}
-
-static bool ftgmac100_rxdes_frame_too_long(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FTL);
-}
-
-static bool ftgmac100_rxdes_runt(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RUNT);
-}
-
-static bool ftgmac100_rxdes_odd_nibble(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ODD_NB);
-}
-
-static unsigned int ftgmac100_rxdes_data_length(struct ftgmac100_rxdes *rxdes)
-{
- return le32_to_cpu(rxdes->rxdes0) & FTGMAC100_RXDES0_VDBC;
-}
-
-static bool ftgmac100_rxdes_multicast(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_MULTICAST);
-}
-
-static void ftgmac100_rxdes_set_end_of_ring(const struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes)
-{
- rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask);
-}
-
-static void ftgmac100_rxdes_set_dma_addr(struct ftgmac100_rxdes *rxdes,
- dma_addr_t addr)
-{
- rxdes->rxdes3 = cpu_to_le32(addr);
-}
-
-static dma_addr_t ftgmac100_rxdes_get_dma_addr(struct ftgmac100_rxdes *rxdes)
+static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry,
+ struct ftgmac100_rxdes *rxdes, gfp_t gfp)
{
- return le32_to_cpu(rxdes->rxdes3);
-}
-
-static bool ftgmac100_rxdes_is_tcp(struct ftgmac100_rxdes *rxdes)
-{
- return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) ==
- cpu_to_le32(FTGMAC100_RXDES1_PROT_TCPIP);
-}
-
-static bool ftgmac100_rxdes_is_udp(struct ftgmac100_rxdes *rxdes)
-{
- return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) ==
- cpu_to_le32(FTGMAC100_RXDES1_PROT_UDPIP);
-}
+ struct net_device *netdev = priv->netdev;
+ struct sk_buff *skb;
+ dma_addr_t map;
+ int err;
-static bool ftgmac100_rxdes_tcpcs_err(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR);
-}
+ skb = netdev_alloc_skb_ip_align(netdev, RX_BUF_SIZE);
+ if (unlikely(!skb)) {
+ if (net_ratelimit())
+ netdev_warn(netdev, "failed to allocate rx skb\n");
+ err = -ENOMEM;
+ map = priv->rx_scratch_dma;
+ } else {
+ map = dma_map_single(priv->dev, skb->data, RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, map))) {
+ if (net_ratelimit())
+ netdev_err(netdev, "failed to map rx page\n");
+ dev_kfree_skb_any(skb);
+ map = priv->rx_scratch_dma;
+ skb = NULL;
+ err = -ENOMEM;
+ }
+ }
-static bool ftgmac100_rxdes_udpcs_err(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_UDP_CHKSUM_ERR);
-}
+ /* Store skb */
+ priv->rx_skbs[entry] = skb;
-static bool ftgmac100_rxdes_ipcs_err(struct ftgmac100_rxdes *rxdes)
-{
- return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_IP_CHKSUM_ERR);
-}
+ /* Store DMA address into RX desc */
+ rxdes->rxdes3 = cpu_to_le32(map);
-static inline struct page **ftgmac100_rxdes_page_slot(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes)
-{
- return &priv->rx_pages[rxdes - priv->descs->rxdes];
-}
+ /* Ensure the above is ordered vs clearing the OWN bit */
+ dma_wmb();
-/*
- * rxdes2 is not used by hardware. We use it to keep track of page.
- * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
- */
-static void ftgmac100_rxdes_set_page(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes,
- struct page *page)
-{
- *ftgmac100_rxdes_page_slot(priv, rxdes) = page;
-}
+ /* Clean status (which resets own bit) */
+ if (entry == (RX_QUEUE_ENTRIES - 1))
+ rxdes->rxdes0 = cpu_to_le32(priv->rxdes0_edorr_mask);
+ else
+ rxdes->rxdes0 = 0;
-static struct page *ftgmac100_rxdes_get_page(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes)
-{
- return *ftgmac100_rxdes_page_slot(priv, rxdes);
+ return 0;
}
static int ftgmac100_next_rx_pointer(int pointer)
@@ -406,183 +328,143 @@ static int ftgmac100_next_rx_pointer(int pointer)
return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
}
-static void ftgmac100_rx_pointer_advance(struct ftgmac100 *priv)
-{
- priv->rx_pointer = ftgmac100_next_rx_pointer(priv->rx_pointer);
-}
-
-static struct ftgmac100_rxdes *ftgmac100_current_rxdes(struct ftgmac100 *priv)
-{
- return &priv->descs->rxdes[priv->rx_pointer];
-}
-
-static struct ftgmac100_rxdes *
-ftgmac100_rx_locate_first_segment(struct ftgmac100 *priv)
-{
- struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv);
-
- while (ftgmac100_rxdes_packet_ready(rxdes)) {
- if (ftgmac100_rxdes_first_segment(rxdes))
- return rxdes;
-
- ftgmac100_rxdes_set_dma_own(priv, rxdes);
- ftgmac100_rx_pointer_advance(priv);
- rxdes = ftgmac100_current_rxdes(priv);
- }
-
- return NULL;
-}
-
-static bool ftgmac100_rx_packet_error(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes)
+static void ftgmac100_rx_packet_error(struct ftgmac100 *priv, u32 status)
{
struct net_device *netdev = priv->netdev;
- bool error = false;
-
- if (unlikely(ftgmac100_rxdes_rx_error(rxdes))) {
- if (net_ratelimit())
- netdev_info(netdev, "rx err\n");
+ if (status & FTGMAC100_RXDES0_RX_ERR)
netdev->stats.rx_errors++;
- error = true;
- }
-
- if (unlikely(ftgmac100_rxdes_crc_error(rxdes))) {
- if (net_ratelimit())
- netdev_info(netdev, "rx crc err\n");
+ if (status & FTGMAC100_RXDES0_CRC_ERR)
netdev->stats.rx_crc_errors++;
- error = true;
- } else if (unlikely(ftgmac100_rxdes_ipcs_err(rxdes))) {
- if (net_ratelimit())
- netdev_info(netdev, "rx IP checksum err\n");
-
- error = true;
- }
-
- if (unlikely(ftgmac100_rxdes_frame_too_long(rxdes))) {
- if (net_ratelimit())
- netdev_info(netdev, "rx frame too long\n");
-
- netdev->stats.rx_length_errors++;
- error = true;
- } else if (unlikely(ftgmac100_rxdes_runt(rxdes))) {
- if (net_ratelimit())
- netdev_info(netdev, "rx runt\n");
-
- netdev->stats.rx_length_errors++;
- error = true;
- } else if (unlikely(ftgmac100_rxdes_odd_nibble(rxdes))) {
- if (net_ratelimit())
- netdev_info(netdev, "rx odd nibble\n");
+ if (status & (FTGMAC100_RXDES0_FTL |
+ FTGMAC100_RXDES0_RUNT |
+ FTGMAC100_RXDES0_RX_ODD_NB))
netdev->stats.rx_length_errors++;
- error = true;
- }
-
- return error;
}
-static void ftgmac100_rx_drop_packet(struct ftgmac100 *priv)
+static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
{
struct net_device *netdev = priv->netdev;
- struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv);
- bool done = false;
+ struct ftgmac100_rxdes *rxdes;
+ struct sk_buff *skb;
+ unsigned int pointer, size;
+ u32 status, csum_vlan;
+ dma_addr_t map;
- if (net_ratelimit())
- netdev_dbg(netdev, "drop packet %p\n", rxdes);
+ /* Grab next RX descriptor */
+ pointer = priv->rx_pointer;
+ rxdes = &priv->descs->rxdes[pointer];
- do {
- if (ftgmac100_rxdes_last_segment(rxdes))
- done = true;
+ /* Grab descriptor status */
+ status = le32_to_cpu(rxdes->rxdes0);
- ftgmac100_rxdes_set_dma_own(priv, rxdes);
- ftgmac100_rx_pointer_advance(priv);
- rxdes = ftgmac100_current_rxdes(priv);
- } while (!done && ftgmac100_rxdes_packet_ready(rxdes));
+ /* Do we have a packet ? */
+ if (!(status & FTGMAC100_RXDES0_RXPKT_RDY))
+ return false;
- netdev->stats.rx_dropped++;
-}
+ /* Order subsequent reads with the test for the ready bit */
+ dma_rmb();
-static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
-{
- struct net_device *netdev = priv->netdev;
- struct ftgmac100_rxdes *rxdes;
- struct sk_buff *skb;
- bool done = false;
+ /* We don't cope with fragmented RX packets */
+ if (unlikely(!(status & FTGMAC100_RXDES0_FRS) ||
+ !(status & FTGMAC100_RXDES0_LRS)))
+ goto drop;
- rxdes = ftgmac100_rx_locate_first_segment(priv);
- if (!rxdes)
- return false;
+ /* Grab received size and csum vlan field in the descriptor */
+ size = status & FTGMAC100_RXDES0_VDBC;
+ csum_vlan = le32_to_cpu(rxdes->rxdes1);
- if (unlikely(ftgmac100_rx_packet_error(priv, rxdes))) {
- ftgmac100_rx_drop_packet(priv);
- return true;
+ /* Any error (other than csum offload) flagged ? */
+ if (unlikely(status & RXDES0_ANY_ERROR)) {
+ /* Correct for incorrect flagging of runt packets
+ * with vlan tags... Just accept a runt packet that
+ * has been flagged as vlan and whose size is at
+ * least 60 bytes.
+ */
+ if ((status & FTGMAC100_RXDES0_RUNT) &&
+ (csum_vlan & FTGMAC100_RXDES1_VLANTAG_AVAIL) &&
+ (size >= 60))
+ status &= ~FTGMAC100_RXDES0_RUNT;
+
+ /* Any error still in there ? */
+ if (status & RXDES0_ANY_ERROR) {
+ ftgmac100_rx_packet_error(priv, status);
+ goto drop;
+ }
}
- /* start processing */
- skb = netdev_alloc_skb_ip_align(netdev, 128);
- if (unlikely(!skb)) {
- if (net_ratelimit())
- netdev_err(netdev, "rx skb alloc failed\n");
-
- ftgmac100_rx_drop_packet(priv);
- return true;
+ /* If the packet had no skb (failed to allocate earlier)
+ * then try to allocate one and skip
+ */
+ skb = priv->rx_skbs[pointer];
+ if (!unlikely(skb)) {
+ ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC);
+ goto drop;
}
- if (unlikely(ftgmac100_rxdes_multicast(rxdes)))
+ if (unlikely(status & FTGMAC100_RXDES0_MULTICAST))
netdev->stats.multicast++;
- /*
- * It seems that HW does checksum incorrectly with fragmented packets,
- * so we are conservative here - if HW checksum error, let software do
- * the checksum again.
+ /* If the HW found checksum errors, bounce it to software.
+ *
+ * If we didn't, we need to see if the packet was recognized
+ * by HW as one of the supported checksummed protocols before
+ * we accept the HW test results.
*/
- if ((ftgmac100_rxdes_is_tcp(rxdes) && !ftgmac100_rxdes_tcpcs_err(rxdes)) ||
- (ftgmac100_rxdes_is_udp(rxdes) && !ftgmac100_rxdes_udpcs_err(rxdes)))
- skb->ip_summed = CHECKSUM_UNNECESSARY;
-
- do {
- dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes);
- struct page *page = ftgmac100_rxdes_get_page(priv, rxdes);
- unsigned int size;
-
- dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+ if (netdev->features & NETIF_F_RXCSUM) {
+ u32 err_bits = FTGMAC100_RXDES1_TCP_CHKSUM_ERR |
+ FTGMAC100_RXDES1_UDP_CHKSUM_ERR |
+ FTGMAC100_RXDES1_IP_CHKSUM_ERR;
+ if ((csum_vlan & err_bits) ||
+ !(csum_vlan & FTGMAC100_RXDES1_PROT_MASK))
+ skb->ip_summed = CHECKSUM_NONE;
+ else
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
- size = ftgmac100_rxdes_data_length(rxdes);
- skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, size);
+ /* Transfer received size to skb */
+ skb_put(skb, size);
- skb->len += size;
- skb->data_len += size;
- skb->truesize += PAGE_SIZE;
+ /* Tear down DMA mapping, do necessary cache management */
+ map = le32_to_cpu(rxdes->rxdes3);
- if (ftgmac100_rxdes_last_segment(rxdes))
- done = true;
+#if defined(CONFIG_ARM) && !defined(CONFIG_ARM_DMA_USE_IOMMU)
+ /* When we don't have an iommu, we can save cycles by not
+ * invalidating the cache for the part of the packet that
+ * wasn't received.
+ */
+ dma_unmap_single(priv->dev, map, size, DMA_FROM_DEVICE);
+#else
+ dma_unmap_single(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+#endif
- ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
- ftgmac100_rx_pointer_advance(priv);
- rxdes = ftgmac100_current_rxdes(priv);
- } while (!done);
+ /* Resplenish rx ring */
+ ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC);
+ priv->rx_pointer = ftgmac100_next_rx_pointer(pointer);
- /* Small frames are copied into linear part of skb to free one page */
- if (skb->len <= 128) {
- skb->truesize -= PAGE_SIZE;
- __pskb_pull_tail(skb, skb->len);
- } else {
- /* We pull the minimum amount into linear part */
- __pskb_pull_tail(skb, ETH_HLEN);
- }
skb->protocol = eth_type_trans(skb, netdev);
netdev->stats.rx_packets++;
- netdev->stats.rx_bytes += skb->len;
+ netdev->stats.rx_bytes += size;
/* push packet to protocol stack */
- napi_gro_receive(&priv->napi, skb);
+ if (skb->ip_summed == CHECKSUM_NONE)
+ netif_receive_skb(skb);
+ else
+ napi_gro_receive(&priv->napi, skb);
(*processed)++;
return true;
+
+ drop:
+ /* Clean rxdes0 (which resets own bit) */
+ rxdes->rxdes0 = cpu_to_le32(status & priv->rxdes0_edorr_mask);
+ priv->rx_pointer = ftgmac100_next_rx_pointer(pointer);
+ netdev->stats.rx_dropped++;
+ return true;
}
static void ftgmac100_txdes_reset(const struct ftgmac100 *priv,
@@ -793,34 +675,6 @@ static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb,
return NETDEV_TX_OK;
}
-static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes, gfp_t gfp)
-{
- struct net_device *netdev = priv->netdev;
- struct page *page;
- dma_addr_t map;
-
- page = alloc_page(gfp);
- if (!page) {
- if (net_ratelimit())
- netdev_err(netdev, "failed to allocate rx page\n");
- return -ENOMEM;
- }
-
- map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(priv->dev, map))) {
- if (net_ratelimit())
- netdev_err(netdev, "failed to map rx page\n");
- __free_page(page);
- return -ENOMEM;
- }
-
- ftgmac100_rxdes_set_page(priv, rxdes, page);
- ftgmac100_rxdes_set_dma_addr(rxdes, map);
- ftgmac100_rxdes_set_dma_own(priv, rxdes);
- return 0;
-}
-
static void ftgmac100_free_buffers(struct ftgmac100 *priv)
{
int i;
@@ -828,14 +682,15 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
/* Free all RX buffers */
for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
- struct page *page = ftgmac100_rxdes_get_page(priv, rxdes);
- dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes);
+ struct sk_buff *skb = priv->rx_skbs[i];
+ dma_addr_t map = le32_to_cpu(rxdes->rxdes3);
- if (!page)
+ if (!skb)
continue;
- dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
- __free_page(page);
+ priv->rx_skbs[i] = NULL;
+ dma_unmap_single(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+ dev_kfree_skb_any(skb);
}
/* Free all TX buffers */
@@ -858,6 +713,11 @@ static void ftgmac100_free_rings(struct ftgmac100 *priv)
if (priv->descs)
dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs),
priv->descs, priv->descs_dma_addr);
+
+ /* Free scratch packet buffer */
+ if (priv->rx_scratch)
+ dma_free_coherent(priv->dev, RX_BUF_SIZE,
+ priv->rx_scratch, priv->rx_scratch_dma);
}
static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
@@ -869,17 +729,30 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
if (!priv->descs)
return -ENOMEM;
+ /* Allocate scratch packet buffer */
+ priv->rx_scratch = dma_alloc_coherent(priv->dev,
+ RX_BUF_SIZE,
+ &priv->rx_scratch_dma,
+ GFP_KERNEL);
+ if (!priv->rx_scratch)
+ return -ENOMEM;
+
return 0;
}
static void ftgmac100_init_rings(struct ftgmac100 *priv)
{
+ struct ftgmac100_rxdes *rxdes;
int i;
/* Initialize RX ring */
- for (i = 0; i < RX_QUEUE_ENTRIES; i++)
- priv->descs->rxdes[i].rxdes0 = 0;
- ftgmac100_rxdes_set_end_of_ring(priv, &priv->descs->rxdes[i - 1]);
+ for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+ rxdes = &priv->descs->rxdes[i];
+ rxdes->rxdes0 = 0;
+ rxdes->rxdes3 = cpu_to_le32(priv->rx_scratch_dma);
+ }
+ /* Mark the end of the ring */
+ rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask);
/* Initialize TX ring */
for (i = 0; i < TX_QUEUE_ENTRIES; i++)
@@ -894,7 +767,7 @@ static int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv)
for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
- if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL))
+ if (ftgmac100_alloc_rx_buf(priv, i, rxdes, GFP_KERNEL))
return -ENOMEM;
}
return 0;
@@ -1095,6 +968,14 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static bool ftgmac100_check_rx(struct ftgmac100 *priv)
+{
+ struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[priv->rx_pointer];
+
+ /* Do we have a packet ? */
+ return !!(rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY));
+}
+
static int ftgmac100_poll(struct napi_struct *napi, int budget)
{
struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi);
@@ -1134,8 +1015,7 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
*/
iowrite32(FTGMAC100_INT_RXTX,
priv->base + FTGMAC100_OFFSET_ISR);
- if (ftgmac100_rxdes_packet_ready
- (ftgmac100_current_rxdes(priv)) || priv->tx_pending)
+ if (ftgmac100_check_rx(priv) || priv->tx_pending)
return budget;
/* deschedule NAPI */
@@ -1545,7 +1425,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
* when NCSI is enabled on the interface. It doesn't work
* in that case.
*/
- netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
+ netdev->features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_GRO;
if (priv->use_ncsi &&
of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL))
netdev->features &= ~NETIF_F_IP_CSUM;
diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/faraday/ftgmac100.h
index c4d5cc11062f..9124785a4ab9 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.h
+++ b/drivers/net/ethernet/faraday/ftgmac100.h
@@ -227,10 +227,10 @@ struct ftgmac100_txdes {
* Receive descriptor, aligned to 16 bytes
*/
struct ftgmac100_rxdes {
- unsigned int rxdes0;
- unsigned int rxdes1;
- unsigned int rxdes2; /* not used by HW */
- unsigned int rxdes3; /* RXBUF_BADR */
+ __le32 rxdes0; /* Control & status bits */
+ __le32 rxdes1; /* Checksum and vlan status */
+ __le32 rxdes2; /* length/type on AST2500 */
+ __le32 rxdes3; /* DMA buffer address */
} __attribute__ ((aligned(16)));
#define FTGMAC100_RXDES0_VDBC 0x3fff
@@ -248,6 +248,14 @@ struct ftgmac100_rxdes {
#define FTGMAC100_RXDES0_FRS (1 << 29)
#define FTGMAC100_RXDES0_RXPKT_RDY (1 << 31)
+/* Errors we care about for dropping packets */
+#define RXDES0_ANY_ERROR ( \
+ FTGMAC100_RXDES0_RX_ERR | \
+ FTGMAC100_RXDES0_CRC_ERR | \
+ FTGMAC100_RXDES0_FTL | \
+ FTGMAC100_RXDES0_RUNT | \
+ FTGMAC100_RXDES0_RX_ODD_NB)
+
#define FTGMAC100_RXDES1_VLANTAG_CI 0xffff
#define FTGMAC100_RXDES1_PROT_MASK (0x3 << 20)
#define FTGMAC100_RXDES1_PROT_NONIP (0x0 << 20)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index 0f9cd92fdc5e..508923f39ccf 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -1929,7 +1929,7 @@ static struct net_device_stats *emac_stats(struct net_device *ndev)
struct emac_instance *dev = netdev_priv(ndev);
struct emac_stats *st = &dev->stats;
struct emac_error_stats *est = &dev->estats;
- struct net_device_stats *nst = &dev->nstats;
+ struct net_device_stats *nst = &ndev->stats;
unsigned long flags;
DBG2(dev, "stats" NL);
diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 0710a6685489..f10e156641d5 100644
--- a/drivers/net/ethernet/ibm/emac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -265,7 +265,6 @@ struct emac_instance {
/* Stats
*/
struct emac_error_stats estats;
- struct net_device_stats nstats;
struct emac_stats stats;
/* Misc
diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c
index 191028b1489b..eb2896fd52a6 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_client.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_client.c
@@ -436,6 +436,12 @@ int i40e_lan_add_device(struct i40e_pf *pf)
pf->hw.pf_id, pf->hw.bus.bus_id,
pf->hw.bus.device, pf->hw.bus.func);
+ /* If a client has already been registered, we need to add an instance
+ * of it to our new LAN device.
+ */
+ if (registered_client)
+ i40e_client_add_instance(pf);
+
/* Since in some cases register may have happened before a device gets
* added, we can schedule a subtask to go initiate the clients if
* they can be launched at probe time.
@@ -459,6 +465,9 @@ int i40e_lan_del_device(struct i40e_pf *pf)
struct i40e_device *ldev, *tmp;
int ret = -ENODEV;
+ /* First, remove any client instance. */
+ i40e_client_del_instance(pf);
+
mutex_lock(&i40e_device_mutex);
list_for_each_entry_safe(ldev, tmp, &i40e_devices, list) {
if (ldev->pf == pf) {
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 703444e92964..d83430faaa41 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -9253,6 +9253,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
u8 broadcast[ETH_ALEN];
u8 mac_addr[ETH_ALEN];
int etherdev_size;
+ netdev_features_t hw_enc_features;
+ netdev_features_t hw_features;
etherdev_size = sizeof(struct i40e_netdev_priv);
netdev = alloc_etherdev_mq(etherdev_size, vsi->alloc_queue_pairs);
@@ -9263,43 +9265,43 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
np = netdev_priv(netdev);
np->vsi = vsi;
- netdev->hw_enc_features |= NETIF_F_SG |
- NETIF_F_IP_CSUM |
- NETIF_F_IPV6_CSUM |
- NETIF_F_HIGHDMA |
- NETIF_F_SOFT_FEATURES |
- NETIF_F_TSO |
- NETIF_F_TSO_ECN |
- NETIF_F_TSO6 |
- NETIF_F_GSO_GRE |
- NETIF_F_GSO_GRE_CSUM |
- NETIF_F_GSO_IPXIP4 |
- NETIF_F_GSO_IPXIP6 |
- NETIF_F_GSO_UDP_TUNNEL |
- NETIF_F_GSO_UDP_TUNNEL_CSUM |
- NETIF_F_GSO_PARTIAL |
- NETIF_F_SCTP_CRC |
- NETIF_F_RXHASH |
- NETIF_F_RXCSUM |
- 0;
+ hw_enc_features = NETIF_F_SG |
+ NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM |
+ NETIF_F_HIGHDMA |
+ NETIF_F_SOFT_FEATURES |
+ NETIF_F_TSO |
+ NETIF_F_TSO_ECN |
+ NETIF_F_TSO6 |
+ NETIF_F_GSO_GRE |
+ NETIF_F_GSO_GRE_CSUM |
+ NETIF_F_GSO_PARTIAL |
+ NETIF_F_GSO_UDP_TUNNEL |
+ NETIF_F_GSO_UDP_TUNNEL_CSUM |
+ NETIF_F_SCTP_CRC |
+ NETIF_F_RXHASH |
+ NETIF_F_RXCSUM |
+ 0;
if (!(pf->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE))
netdev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;
netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM;
+ netdev->hw_enc_features |= hw_enc_features;
+
/* record features VLANs can make use of */
- netdev->vlan_features |= netdev->hw_enc_features |
- NETIF_F_TSO_MANGLEID;
+ netdev->vlan_features |= hw_enc_features | NETIF_F_TSO_MANGLEID;
if (!(pf->flags & I40E_FLAG_MFP_ENABLED))
netdev->hw_features |= NETIF_F_NTUPLE;
+ hw_features = hw_enc_features |
+ NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_CTAG_RX;
- netdev->hw_features |= netdev->hw_enc_features |
- NETIF_F_HW_VLAN_CTAG_TX |
- NETIF_F_HW_VLAN_CTAG_RX;
+ netdev->hw_features |= hw_features;
- netdev->features |= netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
+ netdev->features |= hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID;
if (vsi->type == I40E_VSI_MAIN) {
@@ -11393,6 +11395,11 @@ static void i40e_remove(struct pci_dev *pdev)
if (pf->service_task.func)
cancel_work_sync(&pf->service_task);
+ /* Client close must be called explicitly here because the timer
+ * has been stopped.
+ */
+ i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false);
+
if (pf->flags & I40E_FLAG_SRIOV_ENABLED) {
i40e_free_vfs(pf);
pf->flags &= ~I40E_FLAG_SRIOV_ENABLED;
@@ -11633,6 +11640,11 @@ static void i40e_shutdown(struct pci_dev *pdev)
cancel_work_sync(&pf->service_task);
i40e_fdir_teardown(pf);
+ /* Client close must be called explicitly here because the timer
+ * has been stopped.
+ */
+ i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false);
+
if (pf->wol_en && (pf->flags & I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE))
i40e_enable_mc_magic_wake(pf);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
index 974ba2baf6ea..8552192a5bde 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
@@ -163,7 +163,8 @@ struct i40e_virtchnl_vsi_resource {
#define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000
#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000
#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF 0X00080000
-#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00100000
+#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP 0X00100000
+#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00200000
#define I40E_VF_BASE_MODE_OFFLOADS (I40E_VIRTCHNL_VF_OFFLOAD_L2 | \
I40E_VIRTCHNL_VF_OFFLOAD_VLAN | \
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index d526940ff951..65c95ffc15ec 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -1408,6 +1408,13 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg)
I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2;
}
+ if (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_ENCAP)
+ vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_ENCAP;
+
+ if ((pf->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE) &&
+ (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM))
+ vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM;
+
if (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING) {
if (pf->flags & I40E_FLAG_MFP_ENABLED) {
dev_err(&pf->pdev->dev,
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
index f431fbc4a3e7..c5ad0388c3d5 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
@@ -163,7 +163,8 @@ struct i40e_virtchnl_vsi_resource {
#define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000
#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000
#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF 0X00080000
-#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00100000
+#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP 0X00100000
+#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00200000
#define I40E_VF_BASE_MODE_OFFLOADS (I40E_VIRTCHNL_VF_OFFLOAD_L2 | \
I40E_VIRTCHNL_VF_OFFLOAD_VLAN | \
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
index fb2811c23024..c690aba8e8d1 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
@@ -2401,6 +2401,8 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
struct net_device *netdev = adapter->netdev;
struct i40e_vsi *vsi = &adapter->vsi;
int i;
+ netdev_features_t hw_enc_features;
+ netdev_features_t hw_features;
/* got VF config message back from PF, now we can parse it */
for (i = 0; i < vfres->num_vsis; i++) {
@@ -2412,46 +2414,52 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
return -ENODEV;
}
- netdev->hw_enc_features |= NETIF_F_SG |
- NETIF_F_IP_CSUM |
- NETIF_F_IPV6_CSUM |
- NETIF_F_HIGHDMA |
- NETIF_F_SOFT_FEATURES |
- NETIF_F_TSO |
- NETIF_F_TSO_ECN |
- NETIF_F_TSO6 |
+ hw_enc_features = NETIF_F_SG |
+ NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM |
+ NETIF_F_HIGHDMA |
+ NETIF_F_SOFT_FEATURES |
+ NETIF_F_TSO |
+ NETIF_F_TSO_ECN |
+ NETIF_F_TSO6 |
+ NETIF_F_SCTP_CRC |
+ NETIF_F_RXHASH |
+ NETIF_F_RXCSUM |
+ 0;
+
+ /* advertise to stack only if offloads for encapsulated packets is
+ * supported
+ */
+ if (vfres->vf_offload_flags & I40E_VIRTCHNL_VF_OFFLOAD_ENCAP) {
+ hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_GRE |
NETIF_F_GSO_GRE_CSUM |
NETIF_F_GSO_IPXIP4 |
NETIF_F_GSO_IPXIP6 |
- NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_UDP_TUNNEL_CSUM |
NETIF_F_GSO_PARTIAL |
- NETIF_F_SCTP_CRC |
- NETIF_F_RXHASH |
- NETIF_F_RXCSUM |
0;
- if (!(adapter->flags & I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE))
- netdev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;
-
- netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM;
+ if (!(vfres->vf_offload_flags &
+ I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM))
+ netdev->gso_partial_features |=
+ NETIF_F_GSO_UDP_TUNNEL_CSUM;
+ netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM;
+ netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID;
+ netdev->hw_enc_features |= hw_enc_features;
+ }
/* record features VLANs can make use of */
- netdev->vlan_features |= netdev->hw_enc_features |
- NETIF_F_TSO_MANGLEID;
+ netdev->vlan_features |= hw_enc_features | NETIF_F_TSO_MANGLEID;
/* Write features and hw_features separately to avoid polluting
- * with, or dropping, features that are set when we registgered.
+ * with, or dropping, features that are set when we registered.
*/
- netdev->hw_features |= netdev->hw_enc_features;
+ hw_features = hw_enc_features;
- netdev->features |= netdev->hw_enc_features | I40EVF_VLAN_FEATURES;
- netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID;
+ netdev->hw_features |= hw_features;
- /* disable VLAN features if not supported */
- if (!(vfres->vf_offload_flags & I40E_VIRTCHNL_VF_OFFLOAD_VLAN))
- netdev->features ^= I40EVF_VLAN_FEATURES;
+ netdev->features |= hw_features | I40EVF_VLAN_FEATURES;
adapter->vsi.id = adapter->vsi_res->vsi_id;
@@ -2592,9 +2600,6 @@ static void i40evf_init_task(struct work_struct *work)
goto err_alloc;
}
- if (hw->mac.type == I40E_MAC_X722_VF)
- adapter->flags |= I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE;
-
if (i40evf_process_config(adapter))
goto err_alloc;
adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
index 032be8d3928a..3bccfbb1db14 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
@@ -159,7 +159,9 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter)
I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG |
I40E_VIRTCHNL_VF_OFFLOAD_VLAN |
I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR |
- I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2;
+ I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 |
+ I40E_VIRTCHNL_VF_OFFLOAD_ENCAP |
+ I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM;
adapter->current_op = I40E_VIRTCHNL_OP_GET_VF_RESOURCES;
adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG;
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index bf6317eca2f6..d81d3b6dfd87 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1846,6 +1846,12 @@ static int mtk_hw_init(struct mtk_eth *eth)
/* GE2, Force 1000M/FD, FC ON */
mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(1));
+ /* Indicates CDM to parse the MTK special tag from CPU
+ * which also is working out for untag packets.
+ */
+ val = mtk_r32(eth, MTK_CDMQ_IG_CTRL);
+ mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL);
+
/* Enable RX VLan Offloading */
mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
@@ -2316,6 +2322,8 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
eth->netdev[id]->irq = eth->irq[0];
+ eth->netdev[id]->dev.of_node = np;
+
return 0;
free_netdev:
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 99b1c8e9f16f..996024d02668 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -70,6 +70,10 @@
/* Frame Engine Interrupt Grouping Register */
#define MTK_FE_INT_GRP 0x20
+/* CDMP Ingress Control Register */
+#define MTK_CDMQ_IG_CTRL 0x1400
+#define MTK_CDMQ_STAG_EN BIT(0)
+
/* CDMP Exgress Control Register */
#define MTK_CDMP_EG_CTRL 0x404
diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c
index 6ad44be08b33..c0d7d5eec7e7 100644
--- a/drivers/net/ethernet/moxa/moxart_ether.c
+++ b/drivers/net/ethernet/moxa/moxart_ether.c
@@ -228,8 +228,8 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget)
if (desc0 & (RX_DESC0_ERR | RX_DESC0_CRC_ERR | RX_DESC0_FTL |
RX_DESC0_RUNT | RX_DESC0_ODD_NB)) {
net_dbg_ratelimited("packet error\n");
- priv->stats.rx_dropped++;
- priv->stats.rx_errors++;
+ ndev->stats.rx_dropped++;
+ ndev->stats.rx_errors++;
goto rx_next;
}
@@ -245,8 +245,8 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget)
if (unlikely(!skb)) {
net_dbg_ratelimited("netdev_alloc_skb_ip_align failed\n");
- priv->stats.rx_dropped++;
- priv->stats.rx_errors++;
+ ndev->stats.rx_dropped++;
+ ndev->stats.rx_errors++;
goto rx_next;
}
@@ -256,10 +256,10 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget)
napi_gro_receive(&priv->napi, skb);
rx++;
- priv->stats.rx_packets++;
- priv->stats.rx_bytes += len;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += len;
if (desc0 & RX_DESC0_MULTICAST)
- priv->stats.multicast++;
+ ndev->stats.multicast++;
rx_next:
wmb(); /* prevent setting ownership back too early */
@@ -296,8 +296,8 @@ static void moxart_tx_finished(struct net_device *ndev)
dma_unmap_single(&ndev->dev, priv->tx_mapping[tx_tail],
priv->tx_len[tx_tail], DMA_TO_DEVICE);
- priv->stats.tx_packets++;
- priv->stats.tx_bytes += priv->tx_skb[tx_tail]->len;
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += priv->tx_skb[tx_tail]->len;
dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
priv->tx_skb[tx_tail] = NULL;
@@ -349,7 +349,7 @@ static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
if (moxart_desc_read(desc + TX_REG_OFFSET_DESC0) & TX_DESC0_DMA_OWN) {
net_dbg_ratelimited("no TX space for packet\n");
- priv->stats.tx_dropped++;
+ ndev->stats.tx_dropped++;
goto out_unlock;
}
rmb(); /* ensure data is only read that had TX_DESC0_DMA_OWN cleared */
@@ -400,13 +400,6 @@ out_unlock:
return ret;
}
-static struct net_device_stats *moxart_mac_get_stats(struct net_device *ndev)
-{
- struct moxart_mac_priv_t *priv = netdev_priv(ndev);
-
- return &priv->stats;
-}
-
static void moxart_mac_setmulticast(struct net_device *ndev)
{
struct moxart_mac_priv_t *priv = netdev_priv(ndev);
@@ -456,7 +449,6 @@ static const struct net_device_ops moxart_netdev_ops = {
.ndo_open = moxart_mac_open,
.ndo_stop = moxart_mac_stop,
.ndo_start_xmit = moxart_mac_start_xmit,
- .ndo_get_stats = moxart_mac_get_stats,
.ndo_set_rx_mode = moxart_mac_set_rx_mode,
.ndo_set_mac_address = moxart_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h
index afc32ec998c0..686b8957d5cf 100644
--- a/drivers/net/ethernet/moxa/moxart_ether.h
+++ b/drivers/net/ethernet/moxa/moxart_ether.h
@@ -293,7 +293,6 @@
struct moxart_mac_priv_t {
void __iomem *base;
- struct net_device_stats stats;
unsigned int reg_maccr;
unsigned int reg_imr;
struct napi_struct napi;
diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c
index 9709c8ca0774..159564d8dcdb 100644
--- a/drivers/net/ethernet/nuvoton/w90p910_ether.c
+++ b/drivers/net/ethernet/nuvoton/w90p910_ether.c
@@ -152,7 +152,6 @@ struct w90p910_ether {
struct tran_pdesc *tdesc;
dma_addr_t rdesc_phys;
dma_addr_t tdesc_phys;
- struct net_device_stats stats;
struct platform_device *pdev;
struct resource *res;
struct sk_buff *skb;
@@ -584,15 +583,6 @@ static int w90p910_ether_close(struct net_device *dev)
return 0;
}
-static struct net_device_stats *w90p910_ether_stats(struct net_device *dev)
-{
- struct w90p910_ether *ether;
-
- ether = netdev_priv(dev);
-
- return &ether->stats;
-}
-
static int w90p910_send_frame(struct net_device *dev,
unsigned char *data, int length)
{
@@ -671,10 +661,10 @@ static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id)
ether->finish_tx = 0;
if (txbd->sl & TXDS_TXCP) {
- ether->stats.tx_packets++;
- ether->stats.tx_bytes += txbd->sl & 0xFFFF;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += txbd->sl & 0xFFFF;
} else {
- ether->stats.tx_errors++;
+ dev->stats.tx_errors++;
}
txbd->sl = 0x0;
@@ -730,7 +720,7 @@ static void netdev_rx(struct net_device *dev)
data = ether->rdesc->recv_buf[ether->cur_rx];
skb = netdev_alloc_skb(dev, length + 2);
if (!skb) {
- ether->stats.rx_dropped++;
+ dev->stats.rx_dropped++;
return;
}
@@ -738,24 +728,24 @@ static void netdev_rx(struct net_device *dev)
skb_put(skb, length);
skb_copy_to_linear_data(skb, data, length);
skb->protocol = eth_type_trans(skb, dev);
- ether->stats.rx_packets++;
- ether->stats.rx_bytes += length;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += length;
netif_rx(skb);
} else {
- ether->stats.rx_errors++;
+ dev->stats.rx_errors++;
if (status & RXDS_RP) {
dev_err(&pdev->dev, "rx runt err\n");
- ether->stats.rx_length_errors++;
+ dev->stats.rx_length_errors++;
} else if (status & RXDS_CRCE) {
dev_err(&pdev->dev, "rx crc err\n");
- ether->stats.rx_crc_errors++;
+ dev->stats.rx_crc_errors++;
} else if (status & RXDS_ALIE) {
dev_err(&pdev->dev, "rx alignment err\n");
- ether->stats.rx_frame_errors++;
+ dev->stats.rx_frame_errors++;
} else if (status & RXDS_PTLE) {
dev_err(&pdev->dev, "rx longer err\n");
- ether->stats.rx_over_errors++;
+ dev->stats.rx_over_errors++;
}
}
@@ -912,7 +902,6 @@ static const struct net_device_ops w90p910_ether_netdev_ops = {
.ndo_open = w90p910_ether_open,
.ndo_stop = w90p910_ether_close,
.ndo_start_xmit = w90p910_ether_start_xmit,
- .ndo_get_stats = w90p910_ether_stats,
.ndo_set_rx_mode = w90p910_ether_set_multicast_list,
.ndo_set_mac_address = w90p910_set_mac_address,
.ndo_do_ioctl = w90p910_ether_ioctl,
diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h
index e73a4a5165ee..5e7ad25db8ad 100644
--- a/drivers/net/ethernet/qlogic/qede/qede.h
+++ b/drivers/net/ethernet/qlogic/qede/qede.h
@@ -313,21 +313,24 @@ struct qede_rx_queue {
u8 data_direction;
u8 rxq_id;
+ /* Used once per each NAPI run */
+ u16 num_rx_buffers;
+
+ u16 rx_headroom;
+
u32 rx_buf_size;
u32 rx_buf_seg_size;
- u64 rcv_pkts;
-
struct sw_rx_data *sw_rx_ring;
struct qed_chain rx_bd_ring;
struct qed_chain rx_comp_ring ____cacheline_aligned;
- /* Used once per each NAPI run */
- u16 num_rx_buffers;
-
/* GRO */
struct qede_agg_info tpa_info[ETH_TPA_MAX_AGGS_NUM];
+ /* Used once per each NAPI run */
+ u64 rcv_pkts;
+
u64 rx_hw_errors;
u64 rx_alloc_errors;
u64 rx_ip_frags;
@@ -349,6 +352,11 @@ struct sw_tx_bd {
#define QEDE_TSO_SPLIT_BD BIT(0)
};
+struct sw_tx_xdp {
+ struct page *page;
+ dma_addr_t mapping;
+};
+
struct qede_tx_queue {
u8 is_xdp;
bool is_legacy;
@@ -372,11 +380,11 @@ struct qede_tx_queue {
#define QEDE_TXQ_IDX_TO_XDP(edev, idx) ((idx) + QEDE_MAX_TSS_CNT(edev))
/* Regular Tx requires skb + metadata for release purpose,
- * while XDP requires only the pages themselves.
+ * while XDP requires the pages and the mapped address.
*/
union {
struct sw_tx_bd *skbs;
- struct page **pages;
+ struct sw_tx_xdp *xdp;
} sw_tx_ring;
struct qed_chain tx_pbl;
diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c
index 107c3fda4792..b00a4fce44b7 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_filter.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c
@@ -520,11 +520,6 @@ static int qede_xdp_set(struct qede_dev *edev, struct bpf_prog *prog)
{
struct qede_reload_args args;
- if (prog && prog->xdp_adjust_head) {
- DP_ERR(edev, "Does not support bpf_xdp_adjust_head()\n");
- return -EOPNOTSUPP;
- }
-
/* If we're called, there was already a bpf reference increment */
args.func = &qede_xdp_reload_func;
args.u.new_prog = prog;
@@ -537,6 +532,11 @@ int qede_xdp(struct net_device *dev, struct netdev_xdp *xdp)
{
struct qede_dev *edev = netdev_priv(dev);
+ if (IS_VF(edev)) {
+ DP_NOTICE(edev, "VFs don't support XDP\n");
+ return -EOPNOTSUPP;
+ }
+
switch (xdp->command) {
case XDP_SETUP_PROG:
return qede_xdp_set(edev, xdp->prog);
diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c
index 1e65038c8fc0..961b1d36b9eb 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_fp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c
@@ -87,7 +87,8 @@ int qede_alloc_rx_buffer(struct qede_rx_queue *rxq, bool allow_lazy)
rx_bd = (struct eth_rx_bd *)qed_chain_produce(&rxq->rx_bd_ring);
WARN_ON(!rx_bd);
rx_bd->addr.hi = cpu_to_le32(upper_32_bits(mapping));
- rx_bd->addr.lo = cpu_to_le32(lower_32_bits(mapping));
+ rx_bd->addr.lo = cpu_to_le32(lower_32_bits(mapping) +
+ rxq->rx_headroom);
rxq->sw_rx_prod++;
rxq->filled_buffers++;
@@ -360,7 +361,8 @@ static int qede_xdp_xmit(struct qede_dev *edev, struct qede_fastpath *fp,
metadata->mapping + padding,
length, PCI_DMA_TODEVICE);
- txq->sw_tx_ring.pages[idx] = metadata->data;
+ txq->sw_tx_ring.xdp[idx].page = metadata->data;
+ txq->sw_tx_ring.xdp[idx].mapping = metadata->mapping;
txq->sw_tx_prod++;
/* Mark the fastpath for future XDP doorbell */
@@ -384,19 +386,19 @@ int qede_txq_has_work(struct qede_tx_queue *txq)
static void qede_xdp_tx_int(struct qede_dev *edev, struct qede_tx_queue *txq)
{
- struct eth_tx_1st_bd *bd;
- u16 hw_bd_cons;
+ u16 hw_bd_cons, idx;
hw_bd_cons = le16_to_cpu(*txq->hw_cons_ptr);
barrier();
while (hw_bd_cons != qed_chain_get_cons_idx(&txq->tx_pbl)) {
- bd = (struct eth_tx_1st_bd *)qed_chain_consume(&txq->tx_pbl);
+ qed_chain_consume(&txq->tx_pbl);
+ idx = txq->sw_tx_cons & NUM_TX_BDS_MAX;
- dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(bd),
- PAGE_SIZE, DMA_BIDIRECTIONAL);
- __free_page(txq->sw_tx_ring.pages[txq->sw_tx_cons &
- NUM_TX_BDS_MAX]);
+ dma_unmap_page(&edev->pdev->dev,
+ txq->sw_tx_ring.xdp[idx].mapping,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ __free_page(txq->sw_tx_ring.xdp[idx].page);
txq->sw_tx_cons++;
txq->xmit_pkts++;
@@ -508,7 +510,8 @@ static inline void qede_reuse_page(struct qede_rx_queue *rxq,
new_mapping = curr_prod->mapping + curr_prod->page_offset;
rx_bd_prod->addr.hi = cpu_to_le32(upper_32_bits(new_mapping));
- rx_bd_prod->addr.lo = cpu_to_le32(lower_32_bits(new_mapping));
+ rx_bd_prod->addr.lo = cpu_to_le32(lower_32_bits(new_mapping) +
+ rxq->rx_headroom);
rxq->sw_rx_prod++;
curr_cons->data = NULL;
@@ -624,7 +627,6 @@ static inline void qede_skb_receive(struct qede_dev *edev,
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
napi_gro_receive(&fp->napi, skb);
- rxq->rcv_pkts++;
}
static void qede_set_gro_params(struct qede_dev *edev,
@@ -884,9 +886,9 @@ static inline void qede_tpa_cont(struct qede_dev *edev,
"Strange - TPA cont with more than a single len_list entry\n");
}
-static void qede_tpa_end(struct qede_dev *edev,
- struct qede_fastpath *fp,
- struct eth_fast_path_rx_tpa_end_cqe *cqe)
+static int qede_tpa_end(struct qede_dev *edev,
+ struct qede_fastpath *fp,
+ struct eth_fast_path_rx_tpa_end_cqe *cqe)
{
struct qede_rx_queue *rxq = fp->rxq;
struct qede_agg_info *tpa_info;
@@ -934,11 +936,12 @@ static void qede_tpa_end(struct qede_dev *edev,
tpa_info->state = QEDE_AGG_STATE_NONE;
- return;
+ return 1;
err:
tpa_info->state = QEDE_AGG_STATE_NONE;
dev_kfree_skb_any(tpa_info->skb);
tpa_info->skb = NULL;
+ return 0;
}
static u8 qede_check_notunn_csum(u16 flag)
@@ -990,14 +993,15 @@ static bool qede_rx_xdp(struct qede_dev *edev,
struct qede_rx_queue *rxq,
struct bpf_prog *prog,
struct sw_rx_data *bd,
- struct eth_fast_path_rx_reg_cqe *cqe)
+ struct eth_fast_path_rx_reg_cqe *cqe,
+ u16 *data_offset, u16 *len)
{
- u16 len = le16_to_cpu(cqe->len_on_first_bd);
struct xdp_buff xdp;
enum xdp_action act;
- xdp.data = page_address(bd->data) + cqe->placement_offset;
- xdp.data_end = xdp.data + len;
+ xdp.data_hard_start = page_address(bd->data);
+ xdp.data = xdp.data_hard_start + *data_offset;
+ xdp.data_end = xdp.data + *len;
/* Queues always have a full reset currently, so for the time
* being until there's atomic program replace just mark read
@@ -1007,6 +1011,10 @@ static bool qede_rx_xdp(struct qede_dev *edev,
act = bpf_prog_run_xdp(prog, &xdp);
rcu_read_unlock();
+ /* Recalculate, as XDP might have changed the headers */
+ *data_offset = xdp.data - xdp.data_hard_start;
+ *len = xdp.data_end - xdp.data;
+
if (act == XDP_PASS)
return true;
@@ -1025,7 +1033,7 @@ static bool qede_rx_xdp(struct qede_dev *edev,
/* Now if there's a transmission problem, we'd still have to
* throw current buffer, as replacement was already allocated.
*/
- if (qede_xdp_xmit(edev, fp, bd, cqe->placement_offset, len)) {
+ if (qede_xdp_xmit(edev, fp, bd, *data_offset, *len)) {
dma_unmap_page(rxq->dev, bd->mapping,
PAGE_SIZE, DMA_BIDIRECTIONAL);
__free_page(bd->data);
@@ -1052,7 +1060,7 @@ static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev,
struct sw_rx_data *bd, u16 len,
u16 pad)
{
- unsigned int offset = bd->page_offset;
+ unsigned int offset = bd->page_offset + pad;
struct skb_frag_struct *frag;
struct page *page = bd->data;
unsigned int pull_len;
@@ -1069,7 +1077,7 @@ static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev,
*/
if (len + pad <= edev->rx_copybreak) {
memcpy(skb_put(skb, len),
- page_address(page) + pad + offset, len);
+ page_address(page) + offset, len);
qede_reuse_page(rxq, bd);
goto out;
}
@@ -1077,7 +1085,7 @@ static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev,
frag = &skb_shinfo(skb)->frags[0];
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
- page, pad + offset, len, rxq->rx_buf_seg_size);
+ page, offset, len, rxq->rx_buf_seg_size);
va = skb_frag_address(frag);
pull_len = eth_get_headlen(va, QEDE_RX_HDR_SIZE);
@@ -1178,8 +1186,7 @@ static int qede_rx_process_tpa_cqe(struct qede_dev *edev,
qede_tpa_cont(edev, rxq, &cqe->fast_path_tpa_cont);
return 0;
case ETH_RX_CQE_TYPE_TPA_END:
- qede_tpa_end(edev, fp, &cqe->fast_path_tpa_end);
- return 1;
+ return qede_tpa_end(edev, fp, &cqe->fast_path_tpa_end);
default:
return 0;
}
@@ -1224,12 +1231,13 @@ static int qede_rx_process_cqe(struct qede_dev *edev,
fp_cqe = &cqe->fast_path_regular;
len = le16_to_cpu(fp_cqe->len_on_first_bd);
- pad = fp_cqe->placement_offset;
+ pad = fp_cqe->placement_offset + rxq->rx_headroom;
/* Run eBPF program if one is attached */
if (xdp_prog)
- if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe))
- return 1;
+ if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe,
+ &pad, &len))
+ return 0;
/* If this is an error packet then drop it */
flags = cqe->fast_path_regular.pars_flags.flags;
@@ -1290,8 +1298,8 @@ static int qede_rx_int(struct qede_fastpath *fp, int budget)
{
struct qede_rx_queue *rxq = fp->rxq;
struct qede_dev *edev = fp->edev;
+ int work_done = 0, rcv_pkts = 0;
u16 hw_comp_cons, sw_comp_cons;
- int work_done = 0;
hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr);
sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
@@ -1305,12 +1313,14 @@ static int qede_rx_int(struct qede_fastpath *fp, int budget)
/* Loop to complete all indicated BDs */
while ((sw_comp_cons != hw_comp_cons) && (work_done < budget)) {
- qede_rx_process_cqe(edev, fp, rxq);
+ rcv_pkts += qede_rx_process_cqe(edev, fp, rxq);
qed_chain_recycle_consumed(&rxq->rx_comp_ring);
sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
work_done++;
}
+ rxq->rcv_pkts += rcv_pkts;
+
/* Allocate replacement buffers */
while (rxq->num_rx_buffers - rxq->filled_buffers)
if (qede_alloc_rx_buffer(rxq, false))
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index abd99109e532..8c2baf8b2a08 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -1187,9 +1187,11 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq)
rxq->num_rx_buffers = edev->q_num_rx_buffers;
rxq->rx_buf_size = NET_IP_ALIGN + ETH_OVERHEAD + edev->ndev->mtu;
+ rxq->rx_headroom = edev->xdp_prog ? XDP_PACKET_HEADROOM : 0;
- if (rxq->rx_buf_size > PAGE_SIZE)
- rxq->rx_buf_size = PAGE_SIZE;
+ /* Make sure that the headroom and payload fit in a single page */
+ if (rxq->rx_buf_size + rxq->rx_headroom > PAGE_SIZE)
+ rxq->rx_buf_size = PAGE_SIZE - rxq->rx_headroom;
/* Segment size to spilt a page in multiple equal parts,
* unless XDP is used in which case we'd use the entire page.
@@ -1251,7 +1253,7 @@ static void qede_free_mem_txq(struct qede_dev *edev, struct qede_tx_queue *txq)
{
/* Free the parallel SW ring */
if (txq->is_xdp)
- kfree(txq->sw_tx_ring.pages);
+ kfree(txq->sw_tx_ring.xdp);
else
kfree(txq->sw_tx_ring.skbs);
@@ -1269,9 +1271,9 @@ static int qede_alloc_mem_txq(struct qede_dev *edev, struct qede_tx_queue *txq)
/* Allocate the parallel driver ring for Tx buffers */
if (txq->is_xdp) {
- size = sizeof(*txq->sw_tx_ring.pages) * TX_RING_SIZE;
- txq->sw_tx_ring.pages = kzalloc(size, GFP_KERNEL);
- if (!txq->sw_tx_ring.pages)
+ size = sizeof(*txq->sw_tx_ring.xdp) * TX_RING_SIZE;
+ txq->sw_tx_ring.xdp = kzalloc(size, GFP_KERNEL);
+ if (!txq->sw_tx_ring.xdp)
goto err;
} else {
size = sizeof(*txq->sw_tx_ring.skbs) * TX_RING_SIZE;
diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
index 01a8c020d6db..37881f81319e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
@@ -26,12 +26,15 @@
static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
{
- struct stmmac_priv *priv = (struct stmmac_priv *)p;
- unsigned int entry = priv->cur_tx;
- struct dma_desc *desc = priv->dma_tx + entry;
+ struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)p;
unsigned int nopaged_len = skb_headlen(skb);
+ struct stmmac_priv *priv = tx_q->priv_data;
+ unsigned int entry = tx_q->cur_tx;
unsigned int bmax, des2;
unsigned int i = 1, len;
+ struct dma_desc *desc;
+
+ desc = tx_q->dma_tx + entry;
if (priv->plat->enh_desc)
bmax = BUF_SIZE_8KiB;
@@ -45,16 +48,16 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des2 = cpu_to_le32(des2);
if (dma_mapping_error(priv->device, des2))
return -1;
- priv->tx_skbuff_dma[entry].buf = des2;
- priv->tx_skbuff_dma[entry].len = bmax;
+ tx_q->tx_skbuff_dma[entry].buf = des2;
+ tx_q->tx_skbuff_dma[entry].len = bmax;
/* do not close the descriptor and do not set own bit */
priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE,
0, false);
while (len != 0) {
- priv->tx_skbuff[entry] = NULL;
+ tx_q->tx_skbuff[entry] = NULL;
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
- desc = priv->dma_tx + entry;
+ desc = tx_q->dma_tx + entry;
if (len > bmax) {
des2 = dma_map_single(priv->device,
@@ -63,8 +66,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des2 = cpu_to_le32(des2);
if (dma_mapping_error(priv->device, des2))
return -1;
- priv->tx_skbuff_dma[entry].buf = des2;
- priv->tx_skbuff_dma[entry].len = bmax;
+ tx_q->tx_skbuff_dma[entry].buf = des2;
+ tx_q->tx_skbuff_dma[entry].len = bmax;
priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum,
STMMAC_CHAIN_MODE, 1,
false);
@@ -77,8 +80,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des2 = cpu_to_le32(des2);
if (dma_mapping_error(priv->device, des2))
return -1;
- priv->tx_skbuff_dma[entry].buf = des2;
- priv->tx_skbuff_dma[entry].len = len;
+ tx_q->tx_skbuff_dma[entry].buf = des2;
+ tx_q->tx_skbuff_dma[entry].len = len;
/* last descriptor can be set now */
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum,
STMMAC_CHAIN_MODE, 1,
@@ -87,7 +90,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
}
}
- priv->cur_tx = entry;
+ tx_q->cur_tx = entry;
return entry;
}
@@ -136,32 +139,34 @@ static void stmmac_init_dma_chain(void *des, dma_addr_t phy_addr,
static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
{
- struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
+ struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)priv_ptr;
+ struct stmmac_priv *priv = rx_q->priv_data;
if (priv->hwts_rx_en && !priv->extend_desc)
/* NOTE: Device will overwrite des3 with timestamp value if
* 1588-2002 time stamping is enabled, hence reinitialize it
* to keep explicit chaining in the descriptor.
*/
- p->des3 = cpu_to_le32((unsigned int)(priv->dma_rx_phy +
- (((priv->dirty_rx) + 1) %
+ p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy +
+ (((rx_q->dirty_rx) + 1) %
DMA_RX_SIZE) *
sizeof(struct dma_desc)));
}
static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p)
{
- struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
- unsigned int entry = priv->dirty_tx;
+ struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)priv_ptr;
+ struct stmmac_priv *priv = tx_q->priv_data;
+ unsigned int entry = tx_q->dirty_tx;
- if (priv->tx_skbuff_dma[entry].last_segment && !priv->extend_desc &&
+ if (tx_q->tx_skbuff_dma[entry].last_segment && !priv->extend_desc &&
priv->hwts_tx_en)
/* NOTE: Device will overwrite des3 with timestamp value if
* 1588-2002 time stamping is enabled, hence reinitialize it
* to keep explicit chaining in the descriptor.
*/
- p->des3 = cpu_to_le32((unsigned int)((priv->dma_tx_phy +
- ((priv->dirty_tx + 1) % DMA_TX_SIZE))
+ p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy +
+ ((tx_q->dirty_tx + 1) % DMA_TX_SIZE))
* sizeof(struct dma_desc)));
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
index 452f256ff03f..31213e64513d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
@@ -26,16 +26,17 @@
static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
{
- struct stmmac_priv *priv = (struct stmmac_priv *)p;
- unsigned int entry = priv->cur_tx;
- struct dma_desc *desc;
+ struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)p;
unsigned int nopaged_len = skb_headlen(skb);
+ struct stmmac_priv *priv = tx_q->priv_data;
+ unsigned int entry = tx_q->cur_tx;
unsigned int bmax, len, des2;
+ struct dma_desc *desc;
if (priv->extend_desc)
- desc = (struct dma_desc *)(priv->dma_etx + entry);
+ desc = (struct dma_desc *)(tx_q->dma_etx + entry);
else
- desc = priv->dma_tx + entry;
+ desc = tx_q->dma_tx + entry;
if (priv->plat->enh_desc)
bmax = BUF_SIZE_8KiB;
@@ -52,29 +53,29 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
if (dma_mapping_error(priv->device, des2))
return -1;
- priv->tx_skbuff_dma[entry].buf = des2;
- priv->tx_skbuff_dma[entry].len = bmax;
- priv->tx_skbuff_dma[entry].is_jumbo = true;
+ tx_q->tx_skbuff_dma[entry].buf = des2;
+ tx_q->tx_skbuff_dma[entry].len = bmax;
+ tx_q->tx_skbuff_dma[entry].is_jumbo = true;
desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum,
STMMAC_RING_MODE, 0, false);
- priv->tx_skbuff[entry] = NULL;
+ tx_q->tx_skbuff[entry] = NULL;
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
if (priv->extend_desc)
- desc = (struct dma_desc *)(priv->dma_etx + entry);
+ desc = (struct dma_desc *)(tx_q->dma_etx + entry);
else
- desc = priv->dma_tx + entry;
+ desc = tx_q->dma_tx + entry;
des2 = dma_map_single(priv->device, skb->data + bmax, len,
DMA_TO_DEVICE);
desc->des2 = cpu_to_le32(des2);
if (dma_mapping_error(priv->device, des2))
return -1;
- priv->tx_skbuff_dma[entry].buf = des2;
- priv->tx_skbuff_dma[entry].len = len;
- priv->tx_skbuff_dma[entry].is_jumbo = true;
+ tx_q->tx_skbuff_dma[entry].buf = des2;
+ tx_q->tx_skbuff_dma[entry].len = len;
+ tx_q->tx_skbuff_dma[entry].is_jumbo = true;
desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum,
@@ -85,15 +86,15 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des2 = cpu_to_le32(des2);
if (dma_mapping_error(priv->device, des2))
return -1;
- priv->tx_skbuff_dma[entry].buf = des2;
- priv->tx_skbuff_dma[entry].len = nopaged_len;
- priv->tx_skbuff_dma[entry].is_jumbo = true;
+ tx_q->tx_skbuff_dma[entry].buf = des2;
+ tx_q->tx_skbuff_dma[entry].len = nopaged_len;
+ tx_q->tx_skbuff_dma[entry].is_jumbo = true;
desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum,
STMMAC_RING_MODE, 0, true);
}
- priv->cur_tx = entry;
+ tx_q->cur_tx = entry;
return entry;
}
@@ -125,12 +126,13 @@ static void stmmac_init_desc3(struct dma_desc *p)
static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p)
{
- struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
- unsigned int entry = priv->dirty_tx;
+ struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)priv_ptr;
+ struct stmmac_priv *priv = tx_q->priv_data;
+ unsigned int entry = tx_q->dirty_tx;
/* des3 is only used for jumbo frames tx or time stamping */
- if (unlikely(priv->tx_skbuff_dma[entry].is_jumbo ||
- (priv->tx_skbuff_dma[entry].last_segment &&
+ if (unlikely(tx_q->tx_skbuff_dma[entry].is_jumbo ||
+ (tx_q->tx_skbuff_dma[entry].last_segment &&
!priv->extend_desc && priv->hwts_tx_en)))
p->des3 = 0;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index cd8fb619b1e9..33efe7038cab 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -46,38 +46,51 @@ struct stmmac_tx_info {
bool is_jumbo;
};
-struct stmmac_priv {
- /* Frequently used values are kept adjacent for cache effect */
+/* Frequently used values are kept adjacent for cache effect */
+struct stmmac_tx_queue {
+ u32 queue_index;
+ struct stmmac_priv *priv_data;
struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp;
struct dma_desc *dma_tx;
struct sk_buff **tx_skbuff;
+ struct stmmac_tx_info *tx_skbuff_dma;
unsigned int cur_tx;
unsigned int dirty_tx;
+ dma_addr_t dma_tx_phy;
+ u32 tx_tail_addr;
+};
+
+struct stmmac_rx_queue {
+ u32 queue_index;
+ struct stmmac_priv *priv_data;
+ struct dma_extended_desc *dma_erx;
+ struct dma_desc *dma_rx ____cacheline_aligned_in_smp;
+ struct sk_buff **rx_skbuff;
+ dma_addr_t *rx_skbuff_dma;
+ unsigned int cur_rx;
+ unsigned int dirty_rx;
+ u32 rx_zeroc_thresh;
+ dma_addr_t dma_rx_phy;
+ u32 rx_tail_addr;
+ struct napi_struct napi ____cacheline_aligned_in_smp;
+};
+
+struct stmmac_priv {
+ /* Frequently used values are kept adjacent for cache effect */
u32 tx_count_frames;
u32 tx_coal_frames;
u32 tx_coal_timer;
- struct stmmac_tx_info *tx_skbuff_dma;
- dma_addr_t dma_tx_phy;
+
int tx_coalesce;
int hwts_tx_en;
bool tx_path_in_lpi_mode;
struct timer_list txtimer;
bool tso;
- struct dma_desc *dma_rx ____cacheline_aligned_in_smp;
- struct dma_extended_desc *dma_erx;
- struct sk_buff **rx_skbuff;
- unsigned int cur_rx;
- unsigned int dirty_rx;
unsigned int dma_buf_sz;
unsigned int rx_copybreak;
- unsigned int rx_zeroc_thresh;
u32 rx_riwt;
int hwts_rx_en;
- dma_addr_t *rx_skbuff_dma;
- dma_addr_t dma_rx_phy;
-
- struct napi_struct napi ____cacheline_aligned_in_smp;
void __iomem *ioaddr;
struct net_device *dev;
@@ -85,6 +98,12 @@ struct stmmac_priv {
struct mac_device_info *hw;
spinlock_t lock;
+ /* RX Queue */
+ struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES];
+
+ /* TX Queue */
+ struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES];
+
int oldlink;
int speed;
int oldduplex;
@@ -119,8 +138,6 @@ struct stmmac_priv {
spinlock_t ptp_lock;
void __iomem *mmcaddr;
void __iomem *ptpaddr;
- u32 rx_tail_addr;
- u32 tx_tail_addr;
u32 mss;
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 7cbda41dc996..a89f76b27ea2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -139,6 +139,64 @@ static void stmmac_verify_args(void)
}
/**
+ * stmmac_disable_all_queues - Disable all queues
+ * @priv: driver private structure
+ */
+static void stmmac_disable_all_queues(struct stmmac_priv *priv)
+{
+ u32 rx_queues_cnt = priv->plat->rx_queues_to_use;
+ u32 queue;
+
+ for (queue = 0; queue < rx_queues_cnt; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ napi_disable(&rx_q->napi);
+ }
+}
+
+/**
+ * stmmac_enable_all_queues - Enable all queues
+ * @priv: driver private structure
+ */
+static void stmmac_enable_all_queues(struct stmmac_priv *priv)
+{
+ u32 rx_queues_cnt = priv->plat->rx_queues_to_use;
+ u32 queue;
+
+ for (queue = 0; queue < rx_queues_cnt; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ napi_enable(&rx_q->napi);
+ }
+}
+
+/**
+ * stmmac_stop_all_queues - Stop all queues
+ * @priv: driver private structure
+ */
+static void stmmac_stop_all_queues(struct stmmac_priv *priv)
+{
+ u32 tx_queues_cnt = priv->plat->tx_queues_to_use;
+ u32 queue;
+
+ for (queue = 0; queue < tx_queues_cnt; queue++)
+ netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue));
+}
+
+/**
+ * stmmac_start_all_queues - Start all queues
+ * @priv: driver private structure
+ */
+static void stmmac_start_all_queues(struct stmmac_priv *priv)
+{
+ u32 tx_queues_cnt = priv->plat->tx_queues_to_use;
+ u32 queue;
+
+ for (queue = 0; queue < tx_queues_cnt; queue++)
+ netif_tx_start_queue(netdev_get_tx_queue(priv->dev, queue));
+}
+
+/**
* stmmac_clk_csr_set - dynamically set the MDC clock
* @priv: driver private structure
* Description: this is to dynamically set the MDC clock according to the csr
@@ -185,26 +243,33 @@ static void print_pkt(unsigned char *buf, int len)
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
}
-static inline u32 stmmac_tx_avail(struct stmmac_priv *priv)
+static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue)
{
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
u32 avail;
- if (priv->dirty_tx > priv->cur_tx)
- avail = priv->dirty_tx - priv->cur_tx - 1;
+ if (tx_q->dirty_tx > tx_q->cur_tx)
+ avail = tx_q->dirty_tx - tx_q->cur_tx - 1;
else
- avail = DMA_TX_SIZE - priv->cur_tx + priv->dirty_tx - 1;
+ avail = DMA_TX_SIZE - tx_q->cur_tx + tx_q->dirty_tx - 1;
return avail;
}
-static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv)
+/**
+ * stmmac_rx_dirty - Get RX queue dirty
+ * @priv: driver private structure
+ * @queue: RX queue index
+ */
+static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)
{
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
u32 dirty;
- if (priv->dirty_rx <= priv->cur_rx)
- dirty = priv->cur_rx - priv->dirty_rx;
+ if (rx_q->dirty_rx <= rx_q->cur_rx)
+ dirty = rx_q->cur_rx - rx_q->dirty_rx;
else
- dirty = DMA_RX_SIZE - priv->dirty_rx + priv->cur_rx;
+ dirty = DMA_RX_SIZE - rx_q->dirty_rx + rx_q->cur_rx;
return dirty;
}
@@ -232,9 +297,19 @@ static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv)
*/
static void stmmac_enable_eee_mode(struct stmmac_priv *priv)
{
+ u32 tx_cnt = priv->plat->tx_queues_to_use;
+ u32 queue;
+
+ /* check if all TX queues have the work finished */
+ for (queue = 0; queue < tx_cnt; queue++) {
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+
+ if (tx_q->dirty_tx != tx_q->cur_tx)
+ return; /* still unfinished work */
+ }
+
/* Check and enter in LPI mode */
- if ((priv->dirty_tx == priv->cur_tx) &&
- (priv->tx_path_in_lpi_mode == false))
+ if (!priv->tx_path_in_lpi_mode)
priv->hw->mac->set_eee_mode(priv->hw,
priv->plat->en_tx_lpi_clockgating);
}
@@ -889,22 +964,56 @@ static int stmmac_init_phy(struct net_device *dev)
return 0;
}
-static void stmmac_display_rings(struct stmmac_priv *priv)
+static void stmmac_display_rx_rings(struct stmmac_priv *priv)
{
- void *head_rx, *head_tx;
+ u32 rx_cnt = priv->plat->rx_queues_to_use;
+ void *head_rx;
+ u32 queue;
- if (priv->extend_desc) {
- head_rx = (void *)priv->dma_erx;
- head_tx = (void *)priv->dma_etx;
- } else {
- head_rx = (void *)priv->dma_rx;
- head_tx = (void *)priv->dma_tx;
+ /* Display RX rings */
+ for (queue = 0; queue < rx_cnt; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ pr_info("\tRX Queue %u rings\n", queue);
+
+ if (priv->extend_desc)
+ head_rx = (void *)rx_q->dma_erx;
+ else
+ head_rx = (void *)rx_q->dma_rx;
+
+ /* Display RX ring */
+ priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true);
}
+}
- /* Display Rx ring */
- priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true);
- /* Display Tx ring */
- priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false);
+static void stmmac_display_tx_rings(struct stmmac_priv *priv)
+{
+ u32 tx_cnt = priv->plat->tx_queues_to_use;
+ void *head_tx;
+ u32 queue;
+
+ /* Display TX rings */
+ for (queue = 0; queue < tx_cnt; queue++) {
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+
+ pr_info("\tTX Queue %d rings\n", queue);
+
+ if (priv->extend_desc)
+ head_tx = (void *)tx_q->dma_etx;
+ else
+ head_tx = (void *)tx_q->dma_tx;
+
+ priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false);
+ }
+}
+
+static void stmmac_display_rings(struct stmmac_priv *priv)
+{
+ /* Display RX ring */
+ stmmac_display_rx_rings(priv);
+
+ /* Display TX ring */
+ stmmac_display_tx_rings(priv);
}
static int stmmac_set_bfsize(int mtu, int bufsize)
@@ -924,48 +1033,88 @@ static int stmmac_set_bfsize(int mtu, int bufsize)
}
/**
- * stmmac_clear_descriptors - clear descriptors
+ * stmmac_clear_rx_descriptors - clear RX descriptors
* @priv: driver private structure
- * Description: this function is called to clear the tx and rx descriptors
+ * @queue: RX queue index
+ * Description: this function is called to clear the RX descriptors
* in case of both basic and extended descriptors are used.
*/
-static void stmmac_clear_descriptors(struct stmmac_priv *priv)
+static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue)
{
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
int i;
- /* Clear the Rx/Tx descriptors */
+ /* Clear the RX descriptors */
for (i = 0; i < DMA_RX_SIZE; i++)
if (priv->extend_desc)
- priv->hw->desc->init_rx_desc(&priv->dma_erx[i].basic,
+ priv->hw->desc->init_rx_desc(&rx_q->dma_erx[i].basic,
priv->use_riwt, priv->mode,
(i == DMA_RX_SIZE - 1));
else
- priv->hw->desc->init_rx_desc(&priv->dma_rx[i],
+ priv->hw->desc->init_rx_desc(&rx_q->dma_rx[i],
priv->use_riwt, priv->mode,
(i == DMA_RX_SIZE - 1));
+}
+
+/**
+ * stmmac_clear_tx_descriptors - clear tx descriptors
+ * @priv: driver private structure
+ * @queue: TX queue index.
+ * Description: this function is called to clear the TX descriptors
+ * in case of both basic and extended descriptors are used.
+ */
+static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue)
+{
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+ int i;
+
+ /* Clear the TX descriptors */
for (i = 0; i < DMA_TX_SIZE; i++)
if (priv->extend_desc)
- priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic,
+ priv->hw->desc->init_tx_desc(&tx_q->dma_etx[i].basic,
priv->mode,
(i == DMA_TX_SIZE - 1));
else
- priv->hw->desc->init_tx_desc(&priv->dma_tx[i],
+ priv->hw->desc->init_tx_desc(&tx_q->dma_tx[i],
priv->mode,
(i == DMA_TX_SIZE - 1));
}
/**
+ * stmmac_clear_descriptors - clear descriptors
+ * @priv: driver private structure
+ * Description: this function is called to clear the TX and RX descriptors
+ * in case of both basic and extended descriptors are used.
+ */
+static void stmmac_clear_descriptors(struct stmmac_priv *priv)
+{
+ u32 rx_queue_cnt = priv->plat->rx_queues_to_use;
+ u32 tx_queue_cnt = priv->plat->tx_queues_to_use;
+ u32 queue;
+
+ /* Clear the RX descriptors */
+ for (queue = 0; queue < rx_queue_cnt; queue++)
+ stmmac_clear_rx_descriptors(priv, queue);
+
+ /* Clear the TX descriptors */
+ for (queue = 0; queue < tx_queue_cnt; queue++)
+ stmmac_clear_tx_descriptors(priv, queue);
+}
+
+/**
* stmmac_init_rx_buffers - init the RX descriptor buffer.
* @priv: driver private structure
* @p: descriptor pointer
* @i: descriptor index
- * @flags: gfp flag.
+ * @flags: gfp flag
+ * @queue: RX queue index
* Description: this function is called to allocate a receive buffer, perform
* the DMA mapping and init the descriptor.
*/
static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
- int i, gfp_t flags)
+ int i, gfp_t flags, u32 queue)
{
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
struct sk_buff *skb;
skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags);
@@ -974,20 +1123,20 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
"%s: Rx init fails; skb is NULL\n", __func__);
return -ENOMEM;
}
- priv->rx_skbuff[i] = skb;
- priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
+ rx_q->rx_skbuff[i] = skb;
+ rx_q->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
priv->dma_buf_sz,
DMA_FROM_DEVICE);
- if (dma_mapping_error(priv->device, priv->rx_skbuff_dma[i])) {
+ if (dma_mapping_error(priv->device, rx_q->rx_skbuff_dma[i])) {
netdev_err(priv->dev, "%s: DMA mapping error\n", __func__);
dev_kfree_skb_any(skb);
return -EINVAL;
}
if (priv->synopsys_id >= DWMAC_CORE_4_00)
- p->des0 = cpu_to_le32(priv->rx_skbuff_dma[i]);
+ p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[i]);
else
- p->des2 = cpu_to_le32(priv->rx_skbuff_dma[i]);
+ p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[i]);
if ((priv->hw->mode->init_desc3) &&
(priv->dma_buf_sz == BUF_SIZE_16KiB))
@@ -996,30 +1145,71 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
return 0;
}
-static void stmmac_free_rx_buffers(struct stmmac_priv *priv, int i)
+/**
+ * stmmac_free_rx_buffer - free RX dma buffers
+ * @priv: private structure
+ * @queue: RX queue index
+ * @i: buffer index.
+ */
+static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i)
{
- if (priv->rx_skbuff[i]) {
- dma_unmap_single(priv->device, priv->rx_skbuff_dma[i],
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ if (rx_q->rx_skbuff[i]) {
+ dma_unmap_single(priv->device, rx_q->rx_skbuff_dma[i],
priv->dma_buf_sz, DMA_FROM_DEVICE);
- dev_kfree_skb_any(priv->rx_skbuff[i]);
+ dev_kfree_skb_any(rx_q->rx_skbuff[i]);
}
- priv->rx_skbuff[i] = NULL;
+ rx_q->rx_skbuff[i] = NULL;
}
/**
- * init_dma_desc_rings - init the RX/TX descriptor rings
+ * stmmac_free_tx_buffer - free RX dma buffers
+ * @priv: private structure
+ * @queue: RX queue index
+ * @i: buffer index.
+ */
+static void stmmac_free_tx_buffer(struct stmmac_priv *priv, u32 queue, int i)
+{
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+
+ if (tx_q->tx_skbuff_dma[i].buf) {
+ if (tx_q->tx_skbuff_dma[i].map_as_page)
+ dma_unmap_page(priv->device,
+ tx_q->tx_skbuff_dma[i].buf,
+ tx_q->tx_skbuff_dma[i].len,
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_single(priv->device,
+ tx_q->tx_skbuff_dma[i].buf,
+ tx_q->tx_skbuff_dma[i].len,
+ DMA_TO_DEVICE);
+ }
+
+ if (tx_q->tx_skbuff[i]) {
+ dev_kfree_skb_any(tx_q->tx_skbuff[i]);
+ tx_q->tx_skbuff[i] = NULL;
+ tx_q->tx_skbuff_dma[i].buf = 0;
+ tx_q->tx_skbuff_dma[i].map_as_page = false;
+ }
+}
+
+/**
+ * init_dma_rx_desc_rings - init the RX descriptor rings
* @dev: net device structure
* @flags: gfp flag.
- * Description: this function initializes the DMA RX/TX descriptors
+ * Description: this function initializes the DMA RX descriptors
* and allocates the socket buffers. It supports the chained and ring
* modes.
*/
-static int init_dma_desc_rings(struct net_device *dev, gfp_t flags)
+static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags)
{
- int i;
struct stmmac_priv *priv = netdev_priv(dev);
+ u32 rx_count = priv->plat->rx_queues_to_use;
unsigned int bfsize = 0;
int ret = -ENOMEM;
+ u32 queue;
+ int i;
if (priv->hw->mode->set_16kib_bfsize)
bfsize = priv->hw->mode->set_16kib_bfsize(dev->mtu);
@@ -1029,235 +1219,409 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags)
priv->dma_buf_sz = bfsize;
- netif_dbg(priv, probe, priv->dev,
- "(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n",
- __func__, (u32)priv->dma_rx_phy, (u32)priv->dma_tx_phy);
-
/* RX INITIALIZATION */
netif_dbg(priv, probe, priv->dev,
"SKB addresses:\nskb\t\tskb data\tdma data\n");
- for (i = 0; i < DMA_RX_SIZE; i++) {
- struct dma_desc *p;
- if (priv->extend_desc)
- p = &((priv->dma_erx + i)->basic);
- else
- p = priv->dma_rx + i;
+ for (queue = 0; queue < rx_count; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ netif_dbg(priv, probe, priv->dev,
+ "(%s) dma_rx_phy=0x%08x\n", __func__,
+ (u32)rx_q->dma_rx_phy);
+
+ for (i = 0; i < DMA_RX_SIZE; i++) {
+ struct dma_desc *p;
- ret = stmmac_init_rx_buffers(priv, p, i, flags);
- if (ret)
- goto err_init_rx_buffers;
+ if (priv->extend_desc)
+ p = &((rx_q->dma_erx + i)->basic);
+ else
+ p = rx_q->dma_rx + i;
+
+ ret = stmmac_init_rx_buffers(priv, p, i, flags,
+ queue);
+ if (ret)
+ goto err_init_rx_buffers;
- netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n",
- priv->rx_skbuff[i], priv->rx_skbuff[i]->data,
- (unsigned int)priv->rx_skbuff_dma[i]);
+ netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n",
+ rx_q->rx_skbuff[i], rx_q->rx_skbuff[i]->data,
+ (unsigned int)rx_q->rx_skbuff_dma[i]);
+ }
+
+ rx_q->cur_rx = 0;
+ rx_q->dirty_rx = (unsigned int)(i - DMA_RX_SIZE);
+
+ stmmac_clear_rx_descriptors(priv, queue);
+
+ /* Setup the chained descriptor addresses */
+ if (priv->mode == STMMAC_CHAIN_MODE) {
+ if (priv->extend_desc)
+ priv->hw->mode->init(rx_q->dma_erx,
+ rx_q->dma_rx_phy,
+ DMA_RX_SIZE, 1);
+ else
+ priv->hw->mode->init(rx_q->dma_rx,
+ rx_q->dma_rx_phy,
+ DMA_RX_SIZE, 0);
+ }
}
- priv->cur_rx = 0;
- priv->dirty_rx = (unsigned int)(i - DMA_RX_SIZE);
+
buf_sz = bfsize;
- /* Setup the chained descriptor addresses */
- if (priv->mode == STMMAC_CHAIN_MODE) {
- if (priv->extend_desc) {
- priv->hw->mode->init(priv->dma_erx, priv->dma_rx_phy,
- DMA_RX_SIZE, 1);
- priv->hw->mode->init(priv->dma_etx, priv->dma_tx_phy,
- DMA_TX_SIZE, 1);
- } else {
- priv->hw->mode->init(priv->dma_rx, priv->dma_rx_phy,
- DMA_RX_SIZE, 0);
- priv->hw->mode->init(priv->dma_tx, priv->dma_tx_phy,
- DMA_TX_SIZE, 0);
- }
+ return 0;
+
+err_init_rx_buffers:
+ while (queue >= 0) {
+ while (--i >= 0)
+ stmmac_free_rx_buffer(priv, queue, i);
+
+ if (queue == 0)
+ break;
+
+ i = DMA_RX_SIZE;
+ queue--;
}
- /* TX INITIALIZATION */
- for (i = 0; i < DMA_TX_SIZE; i++) {
- struct dma_desc *p;
- if (priv->extend_desc)
- p = &((priv->dma_etx + i)->basic);
- else
- p = priv->dma_tx + i;
+ return ret;
+}
- if (priv->synopsys_id >= DWMAC_CORE_4_00) {
- p->des0 = 0;
- p->des1 = 0;
- p->des2 = 0;
- p->des3 = 0;
- } else {
- p->des2 = 0;
+/**
+ * init_dma_tx_desc_rings - init the TX descriptor rings
+ * @dev: net device structure.
+ * Description: this function initializes the DMA TX descriptors
+ * and allocates the socket buffers. It supports the chained and ring
+ * modes.
+ */
+static int init_dma_tx_desc_rings(struct net_device *dev)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ u32 tx_queue_cnt = priv->plat->tx_queues_to_use;
+ u32 queue;
+ int i;
+
+ for (queue = 0; queue < tx_queue_cnt; queue++) {
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+
+ netif_dbg(priv, probe, priv->dev,
+ "(%s) dma_tx_phy=0x%08x\n", __func__,
+ (u32)tx_q->dma_tx_phy);
+
+ /* Setup the chained descriptor addresses */
+ if (priv->mode == STMMAC_CHAIN_MODE) {
+ if (priv->extend_desc)
+ priv->hw->mode->init(tx_q->dma_etx,
+ tx_q->dma_tx_phy,
+ DMA_TX_SIZE, 1);
+ else
+ priv->hw->mode->init(tx_q->dma_tx,
+ tx_q->dma_tx_phy,
+ DMA_TX_SIZE, 0);
}
- priv->tx_skbuff_dma[i].buf = 0;
- priv->tx_skbuff_dma[i].map_as_page = false;
- priv->tx_skbuff_dma[i].len = 0;
- priv->tx_skbuff_dma[i].last_segment = false;
- priv->tx_skbuff[i] = NULL;
+ for (i = 0; i < DMA_TX_SIZE; i++) {
+ struct dma_desc *p;
+ if (priv->extend_desc)
+ p = &((tx_q->dma_etx + i)->basic);
+ else
+ p = tx_q->dma_tx + i;
+
+ if (priv->synopsys_id >= DWMAC_CORE_4_00) {
+ p->des0 = 0;
+ p->des1 = 0;
+ p->des2 = 0;
+ p->des3 = 0;
+ } else {
+ p->des2 = 0;
+ }
+
+ tx_q->tx_skbuff_dma[i].buf = 0;
+ tx_q->tx_skbuff_dma[i].map_as_page = false;
+ tx_q->tx_skbuff_dma[i].len = 0;
+ tx_q->tx_skbuff_dma[i].last_segment = false;
+ tx_q->tx_skbuff[i] = NULL;
+ }
+
+ tx_q->dirty_tx = 0;
+ tx_q->cur_tx = 0;
+
+ netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue));
}
- priv->dirty_tx = 0;
- priv->cur_tx = 0;
- netdev_reset_queue(priv->dev);
+ return 0;
+}
+
+/**
+ * init_dma_desc_rings - init the RX/TX descriptor rings
+ * @dev: net device structure
+ * @flags: gfp flag.
+ * Description: this function initializes the DMA RX/TX descriptors
+ * and allocates the socket buffers. It supports the chained and ring
+ * modes.
+ */
+static int init_dma_desc_rings(struct net_device *dev, gfp_t flags)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ int ret;
+
+ ret = init_dma_rx_desc_rings(dev, flags);
+ if (ret)
+ return ret;
+
+ ret = init_dma_tx_desc_rings(dev);
stmmac_clear_descriptors(priv);
if (netif_msg_hw(priv))
stmmac_display_rings(priv);
- return 0;
-err_init_rx_buffers:
- while (--i >= 0)
- stmmac_free_rx_buffers(priv, i);
return ret;
}
-static void dma_free_rx_skbufs(struct stmmac_priv *priv)
+/**
+ * dma_free_rx_skbufs - free RX dma buffers
+ * @priv: private structure
+ * @queue: RX queue index
+ */
+static void dma_free_rx_skbufs(struct stmmac_priv *priv, u32 queue)
{
int i;
for (i = 0; i < DMA_RX_SIZE; i++)
- stmmac_free_rx_buffers(priv, i);
+ stmmac_free_rx_buffer(priv, queue, i);
}
-static void dma_free_tx_skbufs(struct stmmac_priv *priv)
+/**
+ * dma_free_tx_skbufs - free TX dma buffers
+ * @priv: private structure
+ * @queue: TX queue index
+ */
+static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue)
{
int i;
- for (i = 0; i < DMA_TX_SIZE; i++) {
- if (priv->tx_skbuff_dma[i].buf) {
- if (priv->tx_skbuff_dma[i].map_as_page)
- dma_unmap_page(priv->device,
- priv->tx_skbuff_dma[i].buf,
- priv->tx_skbuff_dma[i].len,
- DMA_TO_DEVICE);
- else
- dma_unmap_single(priv->device,
- priv->tx_skbuff_dma[i].buf,
- priv->tx_skbuff_dma[i].len,
- DMA_TO_DEVICE);
- }
+ for (i = 0; i < DMA_TX_SIZE; i++)
+ stmmac_free_tx_buffer(priv, queue, i);
+}
- if (priv->tx_skbuff[i]) {
- dev_kfree_skb_any(priv->tx_skbuff[i]);
- priv->tx_skbuff[i] = NULL;
- priv->tx_skbuff_dma[i].buf = 0;
- priv->tx_skbuff_dma[i].map_as_page = false;
- }
+/**
+ * free_dma_rx_desc_resources - free RX dma desc resources
+ * @priv: private structure
+ */
+static void free_dma_rx_desc_resources(struct stmmac_priv *priv)
+{
+ u32 rx_count = priv->plat->rx_queues_to_use;
+ u32 queue;
+
+ /* Free RX queue resources */
+ for (queue = 0; queue < rx_count; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ /* Release the DMA RX socket buffers */
+ dma_free_rx_skbufs(priv, queue);
+
+ /* Free DMA regions of consistent memory previously allocated */
+ if (!priv->extend_desc)
+ dma_free_coherent(priv->device,
+ DMA_RX_SIZE * sizeof(struct dma_desc),
+ rx_q->dma_rx, rx_q->dma_rx_phy);
+ else
+ dma_free_coherent(priv->device, DMA_RX_SIZE *
+ sizeof(struct dma_extended_desc),
+ rx_q->dma_erx, rx_q->dma_rx_phy);
+
+ kfree(rx_q->rx_skbuff_dma);
+ kfree(rx_q->rx_skbuff);
}
}
/**
- * alloc_dma_desc_resources - alloc TX/RX resources.
+ * free_dma_tx_desc_resources - free TX dma desc resources
+ * @priv: private structure
+ */
+static void free_dma_tx_desc_resources(struct stmmac_priv *priv)
+{
+ u32 tx_count = priv->plat->tx_queues_to_use;
+ u32 queue = 0;
+
+ /* Free TX queue resources */
+ for (queue = 0; queue < tx_count; queue++) {
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+
+ /* Release the DMA TX socket buffers */
+ dma_free_tx_skbufs(priv, queue);
+
+ /* Free DMA regions of consistent memory previously allocated */
+ if (!priv->extend_desc)
+ dma_free_coherent(priv->device,
+ DMA_TX_SIZE * sizeof(struct dma_desc),
+ tx_q->dma_tx, tx_q->dma_tx_phy);
+ else
+ dma_free_coherent(priv->device, DMA_TX_SIZE *
+ sizeof(struct dma_extended_desc),
+ tx_q->dma_etx, tx_q->dma_tx_phy);
+
+ kfree(tx_q->tx_skbuff_dma);
+ kfree(tx_q->tx_skbuff);
+ }
+}
+
+/**
+ * alloc_dma_rx_desc_resources - alloc RX resources.
* @priv: private structure
* Description: according to which descriptor can be used (extend or basic)
* this function allocates the resources for TX and RX paths. In case of
* reception, for example, it pre-allocated the RX socket buffer in order to
* allow zero-copy mechanism.
*/
-static int alloc_dma_desc_resources(struct stmmac_priv *priv)
+static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv)
{
+ u32 rx_count = priv->plat->rx_queues_to_use;
int ret = -ENOMEM;
+ u32 queue;
- priv->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE, sizeof(dma_addr_t),
- GFP_KERNEL);
- if (!priv->rx_skbuff_dma)
- return -ENOMEM;
+ /* RX queues buffers and DMA */
+ for (queue = 0; queue < rx_count; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
- priv->rx_skbuff = kmalloc_array(DMA_RX_SIZE, sizeof(struct sk_buff *),
- GFP_KERNEL);
- if (!priv->rx_skbuff)
- goto err_rx_skbuff;
-
- priv->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE,
- sizeof(*priv->tx_skbuff_dma),
- GFP_KERNEL);
- if (!priv->tx_skbuff_dma)
- goto err_tx_skbuff_dma;
-
- priv->tx_skbuff = kmalloc_array(DMA_TX_SIZE, sizeof(struct sk_buff *),
- GFP_KERNEL);
- if (!priv->tx_skbuff)
- goto err_tx_skbuff;
-
- if (priv->extend_desc) {
- priv->dma_erx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE *
- sizeof(struct
- dma_extended_desc),
- &priv->dma_rx_phy,
- GFP_KERNEL);
- if (!priv->dma_erx)
- goto err_dma;
+ rx_q->queue_index = queue;
+ rx_q->priv_data = priv;
- priv->dma_etx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE *
- sizeof(struct
- dma_extended_desc),
- &priv->dma_tx_phy,
+ rx_q->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE,
+ sizeof(dma_addr_t),
GFP_KERNEL);
- if (!priv->dma_etx) {
- dma_free_coherent(priv->device, DMA_RX_SIZE *
- sizeof(struct dma_extended_desc),
- priv->dma_erx, priv->dma_rx_phy);
- goto err_dma;
- }
- } else {
- priv->dma_rx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE *
- sizeof(struct dma_desc),
- &priv->dma_rx_phy,
- GFP_KERNEL);
- if (!priv->dma_rx)
- goto err_dma;
+ if (!rx_q->rx_skbuff_dma)
+ return -ENOMEM;
- priv->dma_tx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE *
- sizeof(struct dma_desc),
- &priv->dma_tx_phy,
- GFP_KERNEL);
- if (!priv->dma_tx) {
- dma_free_coherent(priv->device, DMA_RX_SIZE *
- sizeof(struct dma_desc),
- priv->dma_rx, priv->dma_rx_phy);
+ rx_q->rx_skbuff = kmalloc_array(DMA_RX_SIZE,
+ sizeof(struct sk_buff *),
+ GFP_KERNEL);
+ if (!rx_q->rx_skbuff)
goto err_dma;
+
+ if (priv->extend_desc) {
+ rx_q->dma_erx = dma_zalloc_coherent(priv->device,
+ DMA_RX_SIZE *
+ sizeof(struct
+ dma_extended_desc),
+ &rx_q->dma_rx_phy,
+ GFP_KERNEL);
+ if (!rx_q->dma_erx)
+ goto err_dma;
+
+ } else {
+ rx_q->dma_rx = dma_zalloc_coherent(priv->device,
+ DMA_RX_SIZE *
+ sizeof(struct
+ dma_desc),
+ &rx_q->dma_rx_phy,
+ GFP_KERNEL);
+ if (!rx_q->dma_rx)
+ goto err_dma;
}
}
return 0;
err_dma:
- kfree(priv->tx_skbuff);
-err_tx_skbuff:
- kfree(priv->tx_skbuff_dma);
-err_tx_skbuff_dma:
- kfree(priv->rx_skbuff);
-err_rx_skbuff:
- kfree(priv->rx_skbuff_dma);
+ free_dma_rx_desc_resources(priv);
+
return ret;
}
-static void free_dma_desc_resources(struct stmmac_priv *priv)
+/**
+ * alloc_dma_tx_desc_resources - alloc TX resources.
+ * @priv: private structure
+ * Description: according to which descriptor can be used (extend or basic)
+ * this function allocates the resources for TX and RX paths. In case of
+ * reception, for example, it pre-allocated the RX socket buffer in order to
+ * allow zero-copy mechanism.
+ */
+static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv)
{
- /* Release the DMA TX/RX socket buffers */
- dma_free_rx_skbufs(priv);
- dma_free_tx_skbufs(priv);
-
- /* Free DMA regions of consistent memory previously allocated */
- if (!priv->extend_desc) {
- dma_free_coherent(priv->device,
- DMA_TX_SIZE * sizeof(struct dma_desc),
- priv->dma_tx, priv->dma_tx_phy);
- dma_free_coherent(priv->device,
- DMA_RX_SIZE * sizeof(struct dma_desc),
- priv->dma_rx, priv->dma_rx_phy);
- } else {
- dma_free_coherent(priv->device, DMA_TX_SIZE *
- sizeof(struct dma_extended_desc),
- priv->dma_etx, priv->dma_tx_phy);
- dma_free_coherent(priv->device, DMA_RX_SIZE *
- sizeof(struct dma_extended_desc),
- priv->dma_erx, priv->dma_rx_phy);
+ u32 tx_count = priv->plat->tx_queues_to_use;
+ int ret = -ENOMEM;
+ u32 queue;
+
+ /* TX queues buffers and DMA */
+ for (queue = 0; queue < tx_count; queue++) {
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+
+ tx_q->queue_index = queue;
+ tx_q->priv_data = priv;
+
+ tx_q->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE,
+ sizeof(*tx_q->tx_skbuff_dma),
+ GFP_KERNEL);
+ if (!tx_q->tx_skbuff_dma)
+ return -ENOMEM;
+
+ tx_q->tx_skbuff = kmalloc_array(DMA_TX_SIZE,
+ sizeof(struct sk_buff *),
+ GFP_KERNEL);
+ if (!tx_q->tx_skbuff)
+ goto err_dma_buffers;
+
+ if (priv->extend_desc) {
+ tx_q->dma_etx = dma_zalloc_coherent(priv->device,
+ DMA_TX_SIZE *
+ sizeof(struct
+ dma_extended_desc),
+ &tx_q->dma_tx_phy,
+ GFP_KERNEL);
+ if (!tx_q->dma_etx)
+ goto err_dma_buffers;
+ } else {
+ tx_q->dma_tx = dma_zalloc_coherent(priv->device,
+ DMA_TX_SIZE *
+ sizeof(struct
+ dma_desc),
+ &tx_q->dma_tx_phy,
+ GFP_KERNEL);
+ if (!tx_q->dma_tx)
+ goto err_dma_buffers;
+ }
}
- kfree(priv->rx_skbuff_dma);
- kfree(priv->rx_skbuff);
- kfree(priv->tx_skbuff_dma);
- kfree(priv->tx_skbuff);
+
+ return 0;
+
+err_dma_buffers:
+ free_dma_tx_desc_resources(priv);
+
+ return ret;
+}
+
+/**
+ * alloc_dma_desc_resources - alloc TX/RX resources.
+ * @priv: private structure
+ * Description: according to which descriptor can be used (extend or basic)
+ * this function allocates the resources for TX and RX paths. In case of
+ * reception, for example, it pre-allocated the RX socket buffer in order to
+ * allow zero-copy mechanism.
+ */
+static int alloc_dma_desc_resources(struct stmmac_priv *priv)
+{
+ /* RX Allocation */
+ int ret = alloc_dma_rx_desc_resources(priv);
+
+ if (ret)
+ return ret;
+
+ ret = alloc_dma_tx_desc_resources(priv);
+
+ return ret;
+}
+
+/**
+ * free_dma_desc_resources - free dma desc resources
+ * @priv: private structure
+ */
+static void free_dma_desc_resources(struct stmmac_priv *priv)
+{
+ /* Release the DMA RX socket buffers */
+ free_dma_rx_desc_resources(priv);
+
+ /* Release the DMA TX socket buffers */
+ free_dma_tx_desc_resources(priv);
}
/**
@@ -1421,26 +1785,28 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
/**
* stmmac_tx_clean - to manage the transmission completion
* @priv: driver private structure
+ * @queue: TX queue index
* Description: it reclaims the transmit resources after transmission completes.
*/
-static void stmmac_tx_clean(struct stmmac_priv *priv)
+static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue)
{
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
unsigned int bytes_compl = 0, pkts_compl = 0;
- unsigned int entry = priv->dirty_tx;
+ unsigned int entry = tx_q->dirty_tx;
netif_tx_lock(priv->dev);
priv->xstats.tx_clean++;
- while (entry != priv->cur_tx) {
- struct sk_buff *skb = priv->tx_skbuff[entry];
+ while (entry != tx_q->cur_tx) {
+ struct sk_buff *skb = tx_q->tx_skbuff[entry];
struct dma_desc *p;
int status;
if (priv->extend_desc)
- p = (struct dma_desc *)(priv->dma_etx + entry);
+ p = (struct dma_desc *)(tx_q->dma_etx + entry);
else
- p = priv->dma_tx + entry;
+ p = tx_q->dma_tx + entry;
status = priv->hw->desc->tx_status(&priv->dev->stats,
&priv->xstats, p,
@@ -1461,48 +1827,51 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)
stmmac_get_tx_hwtstamp(priv, p, skb);
}
- if (likely(priv->tx_skbuff_dma[entry].buf)) {
- if (priv->tx_skbuff_dma[entry].map_as_page)
+ if (likely(tx_q->tx_skbuff_dma[entry].buf)) {
+ if (tx_q->tx_skbuff_dma[entry].map_as_page)
dma_unmap_page(priv->device,
- priv->tx_skbuff_dma[entry].buf,
- priv->tx_skbuff_dma[entry].len,
+ tx_q->tx_skbuff_dma[entry].buf,
+ tx_q->tx_skbuff_dma[entry].len,
DMA_TO_DEVICE);
else
dma_unmap_single(priv->device,
- priv->tx_skbuff_dma[entry].buf,
- priv->tx_skbuff_dma[entry].len,
+ tx_q->tx_skbuff_dma[entry].buf,
+ tx_q->tx_skbuff_dma[entry].len,
DMA_TO_DEVICE);
- priv->tx_skbuff_dma[entry].buf = 0;
- priv->tx_skbuff_dma[entry].len = 0;
- priv->tx_skbuff_dma[entry].map_as_page = false;
+ tx_q->tx_skbuff_dma[entry].buf = 0;
+ tx_q->tx_skbuff_dma[entry].len = 0;
+ tx_q->tx_skbuff_dma[entry].map_as_page = false;
}
if (priv->hw->mode->clean_desc3)
- priv->hw->mode->clean_desc3(priv, p);
+ priv->hw->mode->clean_desc3(tx_q, p);
- priv->tx_skbuff_dma[entry].last_segment = false;
- priv->tx_skbuff_dma[entry].is_jumbo = false;
+ tx_q->tx_skbuff_dma[entry].last_segment = false;
+ tx_q->tx_skbuff_dma[entry].is_jumbo = false;
if (likely(skb != NULL)) {
pkts_compl++;
bytes_compl += skb->len;
dev_consume_skb_any(skb);
- priv->tx_skbuff[entry] = NULL;
+ tx_q->tx_skbuff[entry] = NULL;
}
priv->hw->desc->release_tx_desc(p, priv->mode);
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
}
- priv->dirty_tx = entry;
+ tx_q->dirty_tx = entry;
+
+ netdev_tx_completed_queue(netdev_get_tx_queue(priv->dev, queue),
+ pkts_compl, bytes_compl);
- netdev_completed_queue(priv->dev, pkts_compl, bytes_compl);
+ if (unlikely(netif_tx_queue_stopped(netdev_get_tx_queue(priv->dev,
+ queue))) &&
+ stmmac_tx_avail(priv, queue) > STMMAC_TX_THRESH) {
- if (unlikely(netif_queue_stopped(priv->dev) &&
- stmmac_tx_avail(priv) > STMMAC_TX_THRESH)) {
netif_dbg(priv, tx_done, priv->dev,
"%s: restart transmit\n", __func__);
- netif_wake_queue(priv->dev);
+ netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, queue));
}
if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) {
@@ -1531,27 +1900,29 @@ static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv, u32 chan)
*/
static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan)
{
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan];
int i;
- netif_stop_queue(priv->dev);
+
+ netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan));
stmmac_stop_tx_dma(priv, chan);
- dma_free_tx_skbufs(priv);
+ dma_free_tx_skbufs(priv, chan);
for (i = 0; i < DMA_TX_SIZE; i++)
if (priv->extend_desc)
- priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic,
+ priv->hw->desc->init_tx_desc(&tx_q->dma_etx[i].basic,
priv->mode,
(i == DMA_TX_SIZE - 1));
else
- priv->hw->desc->init_tx_desc(&priv->dma_tx[i],
+ priv->hw->desc->init_tx_desc(&tx_q->dma_tx[i],
priv->mode,
(i == DMA_TX_SIZE - 1));
- priv->dirty_tx = 0;
- priv->cur_tx = 0;
- netdev_reset_queue(priv->dev);
+ tx_q->dirty_tx = 0;
+ tx_q->cur_tx = 0;
+ netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, chan));
stmmac_start_tx_dma(priv, chan);
priv->dev->stats.tx_errors++;
- netif_wake_queue(priv->dev);
+ netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, chan));
}
/**
@@ -1596,12 +1967,14 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
u32 chan;
for (chan = 0; chan < tx_channel_count; chan++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan];
+
status = priv->hw->dma->dma_interrupt(priv->ioaddr,
&priv->xstats, chan);
if (likely((status & handle_rx)) || (status & handle_tx)) {
- if (likely(napi_schedule_prep(&priv->napi))) {
+ if (likely(napi_schedule_prep(&rx_q->napi))) {
stmmac_disable_dma_irq(priv, chan);
- __napi_schedule(&priv->napi);
+ __napi_schedule(&rx_q->napi);
}
}
@@ -1734,6 +2107,8 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
{
u32 rx_channels_count = priv->plat->rx_queues_to_use;
u32 tx_channels_count = priv->plat->tx_queues_to_use;
+ struct stmmac_rx_queue *rx_q;
+ struct stmmac_tx_queue *tx_q;
u32 dummy_dma_rx_phy = 0;
u32 dummy_dma_tx_phy = 0;
u32 chan = 0;
@@ -1761,36 +2136,42 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
/* DMA RX Channel Configuration */
for (chan = 0; chan < rx_channels_count; chan++) {
+ rx_q = &priv->rx_queue[chan];
+
priv->hw->dma->init_rx_chan(priv->ioaddr,
priv->plat->dma_cfg,
- priv->dma_rx_phy, chan);
+ rx_q->dma_rx_phy, chan);
- priv->rx_tail_addr = priv->dma_rx_phy +
+ rx_q->rx_tail_addr = rx_q->dma_rx_phy +
(DMA_RX_SIZE * sizeof(struct dma_desc));
priv->hw->dma->set_rx_tail_ptr(priv->ioaddr,
- priv->rx_tail_addr,
+ rx_q->rx_tail_addr,
chan);
}
/* DMA TX Channel Configuration */
for (chan = 0; chan < tx_channels_count; chan++) {
+ tx_q = &priv->tx_queue[chan];
+
priv->hw->dma->init_chan(priv->ioaddr,
- priv->plat->dma_cfg,
- chan);
+ priv->plat->dma_cfg,
+ chan);
priv->hw->dma->init_tx_chan(priv->ioaddr,
priv->plat->dma_cfg,
- priv->dma_tx_phy, chan);
+ tx_q->dma_tx_phy, chan);
- priv->tx_tail_addr = priv->dma_tx_phy +
+ tx_q->tx_tail_addr = tx_q->dma_tx_phy +
(DMA_TX_SIZE * sizeof(struct dma_desc));
priv->hw->dma->set_tx_tail_ptr(priv->ioaddr,
- priv->tx_tail_addr,
+ tx_q->tx_tail_addr,
chan);
}
} else {
+ rx_q = &priv->rx_queue[chan];
+ tx_q = &priv->tx_queue[chan];
priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg,
- priv->dma_tx_phy, priv->dma_rx_phy, atds);
+ tx_q->dma_tx_phy, rx_q->dma_rx_phy, atds);
}
if (priv->plat->axi && priv->hw->dma->axi)
@@ -1808,8 +2189,12 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
static void stmmac_tx_timer(unsigned long data)
{
struct stmmac_priv *priv = (struct stmmac_priv *)data;
+ u32 tx_queues_count = priv->plat->tx_queues_to_use;
+ u32 queue;
- stmmac_tx_clean(priv);
+ /* let's scan all the tx queues */
+ for (queue = 0; queue < tx_queues_count; queue++)
+ stmmac_tx_clean(priv, queue);
}
/**
@@ -2231,8 +2616,8 @@ static int stmmac_open(struct net_device *dev)
}
}
- napi_enable(&priv->napi);
- netif_start_queue(dev);
+ stmmac_enable_all_queues(priv);
+ stmmac_start_all_queues(priv);
return 0;
@@ -2275,9 +2660,9 @@ static int stmmac_release(struct net_device *dev)
phy_disconnect(dev->phydev);
}
- netif_stop_queue(dev);
+ stmmac_stop_all_queues(priv);
- napi_disable(&priv->napi);
+ stmmac_disable_all_queues(priv);
del_timer_sync(&priv->txtimer);
@@ -2314,22 +2699,24 @@ static int stmmac_release(struct net_device *dev)
* @des: buffer start address
* @total_len: total length to fill in descriptors
* @last_segmant: condition for the last descriptor
+ * @queue: TX queue index
* Description:
* This function fills descriptor and request new descriptors according to
* buffer length to fill
*/
static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des,
- int total_len, bool last_segment)
+ int total_len, bool last_segment, u32 queue)
{
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
struct dma_desc *desc;
- int tmp_len;
u32 buff_size;
+ int tmp_len;
tmp_len = total_len;
while (tmp_len > 0) {
- priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE);
- desc = priv->dma_tx + priv->cur_tx;
+ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE);
+ desc = tx_q->dma_tx + tx_q->cur_tx;
desc->des0 = cpu_to_le32(des + (total_len - tmp_len));
buff_size = tmp_len >= TSO_MAX_BUFF_SIZE ?
@@ -2373,23 +2760,28 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des,
*/
static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
{
- u32 pay_len, mss;
- int tmp_pay_len = 0;
+ struct dma_desc *desc, *first, *mss_desc = NULL;
struct stmmac_priv *priv = netdev_priv(dev);
int nfrags = skb_shinfo(skb)->nr_frags;
+ u32 queue = skb_get_queue_mapping(skb);
unsigned int first_entry, des;
- struct dma_desc *desc, *first, *mss_desc = NULL;
+ struct stmmac_tx_queue *tx_q;
+ int tmp_pay_len = 0;
+ u32 pay_len, mss;
u8 proto_hdr_len;
int i;
+ tx_q = &priv->tx_queue[queue];
+
/* Compute header lengths */
proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
/* Desc availability based on threshold should be enough safe */
- if (unlikely(stmmac_tx_avail(priv) <
+ if (unlikely(stmmac_tx_avail(priv, queue) <
(((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) {
- if (!netif_queue_stopped(dev)) {
- netif_stop_queue(dev);
+ if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {
+ netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,
+ queue));
/* This is a hard error, log it. */
netdev_err(priv->dev,
"%s: Tx Ring full when queue awake\n",
@@ -2404,10 +2796,10 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
/* set new MSS value if needed */
if (mss != priv->mss) {
- mss_desc = priv->dma_tx + priv->cur_tx;
+ mss_desc = tx_q->dma_tx + tx_q->cur_tx;
priv->hw->desc->set_mss(mss_desc, mss);
priv->mss = mss;
- priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE);
+ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE);
}
if (netif_msg_tx_queued(priv)) {
@@ -2417,9 +2809,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
skb->data_len);
}
- first_entry = priv->cur_tx;
+ first_entry = tx_q->cur_tx;
- desc = priv->dma_tx + first_entry;
+ desc = tx_q->dma_tx + first_entry;
first = desc;
/* first descriptor: fill Headers on Buf1 */
@@ -2428,9 +2820,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
if (dma_mapping_error(priv->device, des))
goto dma_map_err;
- priv->tx_skbuff_dma[first_entry].buf = des;
- priv->tx_skbuff_dma[first_entry].len = skb_headlen(skb);
- priv->tx_skbuff[first_entry] = skb;
+ tx_q->tx_skbuff_dma[first_entry].buf = des;
+ tx_q->tx_skbuff_dma[first_entry].len = skb_headlen(skb);
+ tx_q->tx_skbuff[first_entry] = skb;
first->des0 = cpu_to_le32(des);
@@ -2441,7 +2833,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
/* If needed take extra descriptors to fill the remaining payload */
tmp_pay_len = pay_len - TSO_MAX_BUFF_SIZE;
- stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0));
+ stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0), queue);
/* Prepare fragments */
for (i = 0; i < nfrags; i++) {
@@ -2454,22 +2846,22 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
goto dma_map_err;
stmmac_tso_allocator(priv, des, skb_frag_size(frag),
- (i == nfrags - 1));
+ (i == nfrags - 1), queue);
- priv->tx_skbuff_dma[priv->cur_tx].buf = des;
- priv->tx_skbuff_dma[priv->cur_tx].len = skb_frag_size(frag);
- priv->tx_skbuff[priv->cur_tx] = NULL;
- priv->tx_skbuff_dma[priv->cur_tx].map_as_page = true;
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].buf = des;
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].len = skb_frag_size(frag);
+ tx_q->tx_skbuff[tx_q->cur_tx] = NULL;
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].map_as_page = true;
}
- priv->tx_skbuff_dma[priv->cur_tx].last_segment = true;
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].last_segment = true;
- priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE);
+ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE);
- if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) {
+ if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) {
netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n",
__func__);
- netif_stop_queue(dev);
+ netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue));
}
dev->stats.tx_bytes += skb->len;
@@ -2501,7 +2893,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
priv->hw->desc->prepare_tso_tx_desc(first, 1,
proto_hdr_len,
pay_len,
- 1, priv->tx_skbuff_dma[first_entry].last_segment,
+ 1, tx_q->tx_skbuff_dma[first_entry].last_segment,
tcp_hdrlen(skb) / 4, (skb->len - proto_hdr_len));
/* If context desc is used to change MSS */
@@ -2516,20 +2908,20 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
if (netif_msg_pktdata(priv)) {
pr_info("%s: curr=%d dirty=%d f=%d, e=%d, f_p=%p, nfrags %d\n",
- __func__, priv->cur_tx, priv->dirty_tx, first_entry,
- priv->cur_tx, first, nfrags);
+ __func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry,
+ tx_q->cur_tx, first, nfrags);
- priv->hw->desc->display_ring((void *)priv->dma_tx, DMA_TX_SIZE,
+ priv->hw->desc->display_ring((void *)tx_q->dma_tx, DMA_TX_SIZE,
0);
pr_info(">>> frame to be transmitted: ");
print_pkt(skb->data, skb_headlen(skb));
}
- netdev_sent_queue(dev, skb->len);
+ netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
- priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr,
- STMMAC_CHAN0);
+ priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr,
+ queue);
return NETDEV_TX_OK;
@@ -2553,21 +2945,26 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int nopaged_len = skb_headlen(skb);
int i, csum_insertion = 0, is_jumbo = 0;
+ u32 queue = skb_get_queue_mapping(skb);
int nfrags = skb_shinfo(skb)->nr_frags;
unsigned int entry, first_entry;
struct dma_desc *desc, *first;
+ struct stmmac_tx_queue *tx_q;
unsigned int enh_desc;
unsigned int des;
+ tx_q = &priv->tx_queue[queue];
+
/* Manage oversized TCP frames for GMAC4 device */
if (skb_is_gso(skb) && priv->tso) {
if (ip_hdr(skb)->protocol == IPPROTO_TCP)
return stmmac_tso_xmit(skb, dev);
}
- if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) {
- if (!netif_queue_stopped(dev)) {
- netif_stop_queue(dev);
+ if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) {
+ if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {
+ netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,
+ queue));
/* This is a hard error, log it. */
netdev_err(priv->dev,
"%s: Tx Ring full when queue awake\n",
@@ -2579,19 +2976,19 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (priv->tx_path_in_lpi_mode)
stmmac_disable_eee_mode(priv);
- entry = priv->cur_tx;
+ entry = tx_q->cur_tx;
first_entry = entry;
csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
if (likely(priv->extend_desc))
- desc = (struct dma_desc *)(priv->dma_etx + entry);
+ desc = (struct dma_desc *)(tx_q->dma_etx + entry);
else
- desc = priv->dma_tx + entry;
+ desc = tx_q->dma_tx + entry;
first = desc;
- priv->tx_skbuff[first_entry] = skb;
+ tx_q->tx_skbuff[first_entry] = skb;
enh_desc = priv->plat->enh_desc;
/* To program the descriptors according to the size of the frame */
@@ -2600,7 +2997,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(is_jumbo) && likely(priv->synopsys_id <
DWMAC_CORE_4_00)) {
- entry = priv->hw->mode->jumbo_frm(priv, skb, csum_insertion);
+ entry = priv->hw->mode->jumbo_frm(tx_q, skb, csum_insertion);
if (unlikely(entry < 0))
goto dma_map_err;
}
@@ -2613,26 +3010,26 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
if (likely(priv->extend_desc))
- desc = (struct dma_desc *)(priv->dma_etx + entry);
+ desc = (struct dma_desc *)(tx_q->dma_etx + entry);
else
- desc = priv->dma_tx + entry;
+ desc = tx_q->dma_tx + entry;
des = skb_frag_dma_map(priv->device, frag, 0, len,
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
goto dma_map_err; /* should reuse desc w/o issues */
- priv->tx_skbuff[entry] = NULL;
+ tx_q->tx_skbuff[entry] = NULL;
- priv->tx_skbuff_dma[entry].buf = des;
+ tx_q->tx_skbuff_dma[entry].buf = des;
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
desc->des0 = cpu_to_le32(des);
else
desc->des2 = cpu_to_le32(des);
- priv->tx_skbuff_dma[entry].map_as_page = true;
- priv->tx_skbuff_dma[entry].len = len;
- priv->tx_skbuff_dma[entry].last_segment = last_segment;
+ tx_q->tx_skbuff_dma[entry].map_as_page = true;
+ tx_q->tx_skbuff_dma[entry].len = len;
+ tx_q->tx_skbuff_dma[entry].last_segment = last_segment;
/* Prepare the descriptor and set the own bit too */
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion,
@@ -2641,20 +3038,20 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
- priv->cur_tx = entry;
+ tx_q->cur_tx = entry;
if (netif_msg_pktdata(priv)) {
void *tx_head;
netdev_dbg(priv->dev,
"%s: curr=%d dirty=%d f=%d, e=%d, first=%p, nfrags=%d",
- __func__, priv->cur_tx, priv->dirty_tx, first_entry,
+ __func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry,
entry, first, nfrags);
if (priv->extend_desc)
- tx_head = (void *)priv->dma_etx;
+ tx_head = (void *)tx_q->dma_etx;
else
- tx_head = (void *)priv->dma_tx;
+ tx_head = (void *)tx_q->dma_tx;
priv->hw->desc->display_ring(tx_head, DMA_TX_SIZE, false);
@@ -2662,10 +3059,10 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
print_pkt(skb->data, skb->len);
}
- if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) {
+ if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) {
netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n",
__func__);
- netif_stop_queue(dev);
+ netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue));
}
dev->stats.tx_bytes += skb->len;
@@ -2700,14 +3097,14 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (dma_mapping_error(priv->device, des))
goto dma_map_err;
- priv->tx_skbuff_dma[first_entry].buf = des;
+ tx_q->tx_skbuff_dma[first_entry].buf = des;
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
first->des0 = cpu_to_le32(des);
else
first->des2 = cpu_to_le32(des);
- priv->tx_skbuff_dma[first_entry].len = nopaged_len;
- priv->tx_skbuff_dma[first_entry].last_segment = last_segment;
+ tx_q->tx_skbuff_dma[first_entry].len = nopaged_len;
+ tx_q->tx_skbuff_dma[first_entry].last_segment = last_segment;
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en)) {
@@ -2728,13 +3125,13 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
dma_wmb();
}
- netdev_sent_queue(dev, skb->len);
+ netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
if (priv->synopsys_id < DWMAC_CORE_4_00)
priv->hw->dma->enable_dma_transmission(priv->ioaddr);
else
- priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr,
- STMMAC_CHAN0);
+ priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr,
+ queue);
return NETDEV_TX_OK;
@@ -2762,9 +3159,9 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
}
-static inline int stmmac_rx_threshold_count(struct stmmac_priv *priv)
+static inline int stmmac_rx_threshold_count(struct stmmac_rx_queue *rx_q)
{
- if (priv->rx_zeroc_thresh < STMMAC_RX_THRESH)
+ if (rx_q->rx_zeroc_thresh < STMMAC_RX_THRESH)
return 0;
return 1;
@@ -2773,30 +3170,33 @@ static inline int stmmac_rx_threshold_count(struct stmmac_priv *priv)
/**
* stmmac_rx_refill - refill used skb preallocated buffers
* @priv: driver private structure
+ * @queue: RX queue index
* Description : this is to reallocate the skb for the reception process
* that is based on zero-copy.
*/
-static inline void stmmac_rx_refill(struct stmmac_priv *priv)
+static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
{
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+ int dirty = stmmac_rx_dirty(priv, queue);
+ unsigned int entry = rx_q->dirty_rx;
+
int bfsize = priv->dma_buf_sz;
- unsigned int entry = priv->dirty_rx;
- int dirty = stmmac_rx_dirty(priv);
while (dirty-- > 0) {
struct dma_desc *p;
if (priv->extend_desc)
- p = (struct dma_desc *)(priv->dma_erx + entry);
+ p = (struct dma_desc *)(rx_q->dma_erx + entry);
else
- p = priv->dma_rx + entry;
+ p = rx_q->dma_rx + entry;
- if (likely(priv->rx_skbuff[entry] == NULL)) {
+ if (likely(!rx_q->rx_skbuff[entry])) {
struct sk_buff *skb;
skb = netdev_alloc_skb_ip_align(priv->dev, bfsize);
if (unlikely(!skb)) {
/* so for a while no zero-copy! */
- priv->rx_zeroc_thresh = STMMAC_RX_THRESH;
+ rx_q->rx_zeroc_thresh = STMMAC_RX_THRESH;
if (unlikely(net_ratelimit()))
dev_err(priv->device,
"fail to alloc skb entry %d\n",
@@ -2804,28 +3204,28 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv)
break;
}
- priv->rx_skbuff[entry] = skb;
- priv->rx_skbuff_dma[entry] =
+ rx_q->rx_skbuff[entry] = skb;
+ rx_q->rx_skbuff_dma[entry] =
dma_map_single(priv->device, skb->data, bfsize,
DMA_FROM_DEVICE);
if (dma_mapping_error(priv->device,
- priv->rx_skbuff_dma[entry])) {
+ rx_q->rx_skbuff_dma[entry])) {
netdev_err(priv->dev, "Rx DMA map failed\n");
dev_kfree_skb(skb);
break;
}
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) {
- p->des0 = cpu_to_le32(priv->rx_skbuff_dma[entry]);
+ p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]);
p->des1 = 0;
} else {
- p->des2 = cpu_to_le32(priv->rx_skbuff_dma[entry]);
+ p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]);
}
if (priv->hw->mode->refill_desc3)
- priv->hw->mode->refill_desc3(priv, p);
+ priv->hw->mode->refill_desc3(rx_q, p);
- if (priv->rx_zeroc_thresh > 0)
- priv->rx_zeroc_thresh--;
+ if (rx_q->rx_zeroc_thresh > 0)
+ rx_q->rx_zeroc_thresh--;
netif_dbg(priv, rx_status, priv->dev,
"refill entry #%d\n", entry);
@@ -2841,31 +3241,33 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv)
entry = STMMAC_GET_ENTRY(entry, DMA_RX_SIZE);
}
- priv->dirty_rx = entry;
+ rx_q->dirty_rx = entry;
}
/**
* stmmac_rx - manage the receive process
* @priv: driver private structure
- * @limit: napi bugget.
+ * @limit: napi bugget
+ * @queue: RX queue index.
* Description : this the function called by the napi poll method.
* It gets all the frames inside the ring.
*/
-static int stmmac_rx(struct stmmac_priv *priv, int limit)
+static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
{
- unsigned int entry = priv->cur_rx;
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+ unsigned int entry = rx_q->cur_rx;
+ int coe = priv->hw->rx_csum;
unsigned int next_entry;
unsigned int count = 0;
- int coe = priv->hw->rx_csum;
if (netif_msg_rx_status(priv)) {
void *rx_head;
netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__);
if (priv->extend_desc)
- rx_head = (void *)priv->dma_erx;
+ rx_head = (void *)rx_q->dma_erx;
else
- rx_head = (void *)priv->dma_rx;
+ rx_head = (void *)rx_q->dma_rx;
priv->hw->desc->display_ring(rx_head, DMA_RX_SIZE, true);
}
@@ -2875,9 +3277,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
struct dma_desc *np;
if (priv->extend_desc)
- p = (struct dma_desc *)(priv->dma_erx + entry);
+ p = (struct dma_desc *)(rx_q->dma_erx + entry);
else
- p = priv->dma_rx + entry;
+ p = rx_q->dma_rx + entry;
/* read the status of the incoming frame */
status = priv->hw->desc->rx_status(&priv->dev->stats,
@@ -2888,20 +3290,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
count++;
- priv->cur_rx = STMMAC_GET_ENTRY(priv->cur_rx, DMA_RX_SIZE);
- next_entry = priv->cur_rx;
+ rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, DMA_RX_SIZE);
+ next_entry = rx_q->cur_rx;
if (priv->extend_desc)
- np = (struct dma_desc *)(priv->dma_erx + next_entry);
+ np = (struct dma_desc *)(rx_q->dma_erx + next_entry);
else
- np = priv->dma_rx + next_entry;
+ np = rx_q->dma_rx + next_entry;
prefetch(np);
if ((priv->extend_desc) && (priv->hw->desc->rx_extended_status))
priv->hw->desc->rx_extended_status(&priv->dev->stats,
&priv->xstats,
- priv->dma_erx +
+ rx_q->dma_erx +
entry);
if (unlikely(status == discard_frame)) {
priv->dev->stats.rx_errors++;
@@ -2911,9 +3313,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
* them in stmmac_rx_refill() function so that
* device can reuse it.
*/
- priv->rx_skbuff[entry] = NULL;
+ rx_q->rx_skbuff[entry] = NULL;
dma_unmap_single(priv->device,
- priv->rx_skbuff_dma[entry],
+ rx_q->rx_skbuff_dma[entry],
priv->dma_buf_sz,
DMA_FROM_DEVICE);
}
@@ -2961,7 +3363,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
*/
if (unlikely(!priv->plat->has_gmac4 &&
((frame_len < priv->rx_copybreak) ||
- stmmac_rx_threshold_count(priv)))) {
+ stmmac_rx_threshold_count(rx_q)))) {
skb = netdev_alloc_skb_ip_align(priv->dev,
frame_len);
if (unlikely(!skb)) {
@@ -2973,21 +3375,21 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
}
dma_sync_single_for_cpu(priv->device,
- priv->rx_skbuff_dma
+ rx_q->rx_skbuff_dma
[entry], frame_len,
DMA_FROM_DEVICE);
skb_copy_to_linear_data(skb,
- priv->
+ rx_q->
rx_skbuff[entry]->data,
frame_len);
skb_put(skb, frame_len);
dma_sync_single_for_device(priv->device,
- priv->rx_skbuff_dma
+ rx_q->rx_skbuff_dma
[entry], frame_len,
DMA_FROM_DEVICE);
} else {
- skb = priv->rx_skbuff[entry];
+ skb = rx_q->rx_skbuff[entry];
if (unlikely(!skb)) {
netdev_err(priv->dev,
"%s: Inconsistent Rx chain\n",
@@ -2996,12 +3398,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
break;
}
prefetch(skb->data - NET_IP_ALIGN);
- priv->rx_skbuff[entry] = NULL;
- priv->rx_zeroc_thresh++;
+ rx_q->rx_skbuff[entry] = NULL;
+ rx_q->rx_zeroc_thresh++;
skb_put(skb, frame_len);
dma_unmap_single(priv->device,
- priv->rx_skbuff_dma[entry],
+ rx_q->rx_skbuff_dma[entry],
priv->dma_buf_sz,
DMA_FROM_DEVICE);
}
@@ -3023,7 +3425,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
else
skb->ip_summed = CHECKSUM_UNNECESSARY;
- napi_gro_receive(&priv->napi, skb);
+ napi_gro_receive(&rx_q->napi, skb);
priv->dev->stats.rx_packets++;
priv->dev->stats.rx_bytes += frame_len;
@@ -3031,7 +3433,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
entry = next_entry;
}
- stmmac_rx_refill(priv);
+ stmmac_rx_refill(priv, queue);
priv->xstats.rx_pkt_n += count;
@@ -3048,14 +3450,21 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
*/
static int stmmac_poll(struct napi_struct *napi, int budget)
{
- struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi);
+ struct stmmac_rx_queue *rx_q =
+ container_of(napi, struct stmmac_rx_queue, napi);
+ struct stmmac_priv *priv = rx_q->priv_data;
+ u32 tx_count = priv->plat->tx_queues_to_use;
+ u32 chan = rx_q->queue_index;
int work_done = 0;
- u32 chan = STMMAC_CHAN0;
+ u32 queue;
priv->xstats.napi_poll++;
- stmmac_tx_clean(priv);
- work_done = stmmac_rx(priv, budget);
+ /* check all the queues */
+ for (queue = 0; queue < tx_count; queue++)
+ stmmac_tx_clean(priv, queue);
+
+ work_done = stmmac_rx(priv, budget, rx_q->queue_index);
if (work_done < budget) {
napi_complete_done(napi, work_done);
stmmac_enable_dma_irq(priv, chan);
@@ -3074,10 +3483,12 @@ static int stmmac_poll(struct napi_struct *napi, int budget)
static void stmmac_tx_timeout(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
- u32 chan = STMMAC_CHAN0;
+ u32 tx_count = priv->plat->tx_queues_to_use;
+ u32 chan;
/* Clear Tx resources and restart transmitting again */
- stmmac_tx_err(priv, chan);
+ for (chan = 0; chan < tx_count; chan++)
+ stmmac_tx_err(priv, chan);
}
/**
@@ -3216,6 +3627,9 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
if (priv->synopsys_id >= DWMAC_CORE_4_00) {
for (queue = 0; queue < queues_count; queue++) {
+ struct stmmac_rx_queue *rx_q =
+ &priv->rx_queue[queue];
+
status |=
priv->hw->mac->host_mtl_irq_status(priv->hw,
queue);
@@ -3223,7 +3637,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
if (status & CORE_IRQ_MTL_RX_OVERFLOW &&
priv->hw->dma->set_rx_tail_ptr)
priv->hw->dma->set_rx_tail_ptr(priv->ioaddr,
- priv->rx_tail_addr,
+ rx_q->rx_tail_addr,
queue);
}
}
@@ -3323,17 +3737,40 @@ static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
{
struct net_device *dev = seq->private;
struct stmmac_priv *priv = netdev_priv(dev);
+ u32 rx_count = priv->plat->rx_queues_to_use;
+ u32 tx_count = priv->plat->tx_queues_to_use;
+ u32 queue;
- if (priv->extend_desc) {
- seq_printf(seq, "Extended RX descriptor ring:\n");
- sysfs_display_ring((void *)priv->dma_erx, DMA_RX_SIZE, 1, seq);
- seq_printf(seq, "Extended TX descriptor ring:\n");
- sysfs_display_ring((void *)priv->dma_etx, DMA_TX_SIZE, 1, seq);
- } else {
- seq_printf(seq, "RX descriptor ring:\n");
- sysfs_display_ring((void *)priv->dma_rx, DMA_RX_SIZE, 0, seq);
- seq_printf(seq, "TX descriptor ring:\n");
- sysfs_display_ring((void *)priv->dma_tx, DMA_TX_SIZE, 0, seq);
+ for (queue = 0; queue < rx_count; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ seq_printf(seq, "RX Queue %d:\n", queue);
+
+ if (priv->extend_desc) {
+ seq_printf(seq, "Extended descriptor ring:\n");
+ sysfs_display_ring((void *)rx_q->dma_erx,
+ DMA_RX_SIZE, 1, seq);
+ } else {
+ seq_printf(seq, "Descriptor ring:\n");
+ sysfs_display_ring((void *)rx_q->dma_rx,
+ DMA_RX_SIZE, 0, seq);
+ }
+ }
+
+ for (queue = 0; queue < tx_count; queue++) {
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+
+ seq_printf(seq, "TX Queue %d:\n", queue);
+
+ if (priv->extend_desc) {
+ seq_printf(seq, "Extended descriptor ring:\n");
+ sysfs_display_ring((void *)tx_q->dma_etx,
+ DMA_TX_SIZE, 1, seq);
+ } else {
+ seq_printf(seq, "Descriptor ring:\n");
+ sysfs_display_ring((void *)tx_q->dma_tx,
+ DMA_TX_SIZE, 0, seq);
+ }
}
return 0;
@@ -3616,11 +4053,14 @@ int stmmac_dvr_probe(struct device *device,
struct plat_stmmacenet_data *plat_dat,
struct stmmac_resources *res)
{
- int ret = 0;
struct net_device *ndev = NULL;
struct stmmac_priv *priv;
+ int ret = 0;
+ u32 queue;
- ndev = alloc_etherdev(sizeof(struct stmmac_priv));
+ ndev = alloc_etherdev_mqs(sizeof(struct stmmac_priv),
+ MTL_MAX_TX_QUEUES,
+ MTL_MAX_RX_QUEUES);
if (!ndev)
return -ENOMEM;
@@ -3662,6 +4102,10 @@ int stmmac_dvr_probe(struct device *device,
if (ret)
goto error_hw_init;
+ /* Configure real RX and TX queues */
+ ndev->real_num_rx_queues = priv->plat->rx_queues_to_use;
+ ndev->real_num_tx_queues = priv->plat->tx_queues_to_use;
+
ndev->netdev_ops = &stmmac_netdev_ops;
ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
@@ -3711,7 +4155,12 @@ int stmmac_dvr_probe(struct device *device,
"Enable RX Mitigation via HW Watchdog Timer\n");
}
- netif_napi_add(ndev, &priv->napi, stmmac_poll, 64);
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ netif_napi_add(ndev, &rx_q->napi, stmmac_poll,
+ (8 * priv->plat->rx_queues_to_use));
+ }
spin_lock_init(&priv->lock);
@@ -3756,7 +4205,11 @@ error_netdev_register:
priv->hw->pcs != STMMAC_PCS_RTBI)
stmmac_mdio_unregister(ndev);
error_mdio_register:
- netif_napi_del(&priv->napi);
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ netif_napi_del(&rx_q->napi);
+ }
error_hw_init:
free_netdev(ndev);
@@ -3818,9 +4271,9 @@ int stmmac_suspend(struct device *dev)
spin_lock_irqsave(&priv->lock, flags);
netif_device_detach(ndev);
- netif_stop_queue(ndev);
+ stmmac_stop_all_queues(priv);
- napi_disable(&priv->napi);
+ stmmac_disable_all_queues(priv);
/* Stop TX/RX DMA */
stmmac_stop_all_dma(priv);
@@ -3846,6 +4299,31 @@ int stmmac_suspend(struct device *dev)
EXPORT_SYMBOL_GPL(stmmac_suspend);
/**
+ * stmmac_reset_queues_param - reset queue parameters
+ * @dev: device pointer
+ */
+static void stmmac_reset_queues_param(struct stmmac_priv *priv)
+{
+ u32 rx_cnt = priv->plat->rx_queues_to_use;
+ u32 tx_cnt = priv->plat->tx_queues_to_use;
+ u32 queue;
+
+ for (queue = 0; queue < rx_cnt; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
+
+ rx_q->cur_rx = 0;
+ rx_q->dirty_rx = 0;
+ }
+
+ for (queue = 0; queue < tx_cnt; queue++) {
+ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+
+ tx_q->cur_tx = 0;
+ tx_q->dirty_tx = 0;
+ }
+}
+
+/**
* stmmac_resume - resume callback
* @dev: device pointer
* Description: when resume this function is invoked to setup the DMA and CORE
@@ -3885,10 +4363,8 @@ int stmmac_resume(struct device *dev)
spin_lock_irqsave(&priv->lock, flags);
- priv->cur_rx = 0;
- priv->dirty_rx = 0;
- priv->dirty_tx = 0;
- priv->cur_tx = 0;
+ stmmac_reset_queues_param(priv);
+
/* reset private mss value to force mss context settings at
* next tso xmit (only used for gmac4).
*/
@@ -3900,9 +4376,9 @@ int stmmac_resume(struct device *dev)
stmmac_init_tx_coalesce(priv);
stmmac_set_rx_mode(ndev);
- napi_enable(&priv->napi);
+ stmmac_enable_all_queues(priv);
- netif_start_queue(ndev);
+ stmmac_start_all_queues(priv);
spin_unlock_irqrestore(&priv->lock, flags);
diff --git a/drivers/net/ethernet/sun/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c
index c4caf486cbef..3189722110c2 100644
--- a/drivers/net/ethernet/sun/sunbmac.c
+++ b/drivers/net/ethernet/sun/sunbmac.c
@@ -169,7 +169,7 @@ static void bigmac_stop(struct bigmac *bp)
static void bigmac_get_counters(struct bigmac *bp, void __iomem *bregs)
{
- struct net_device_stats *stats = &bp->enet_stats;
+ struct net_device_stats *stats = &bp->dev->stats;
stats->rx_crc_errors += sbus_readl(bregs + BMAC_RCRCECTR);
sbus_writel(0, bregs + BMAC_RCRCECTR);
@@ -774,8 +774,8 @@ static void bigmac_tx(struct bigmac *bp)
if (this->tx_flags & TXD_OWN)
break;
skb = bp->tx_skbs[elem];
- bp->enet_stats.tx_packets++;
- bp->enet_stats.tx_bytes += skb->len;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
dma_unmap_single(&bp->bigmac_op->dev,
this->tx_addr, skb->len,
DMA_TO_DEVICE);
@@ -811,12 +811,12 @@ static void bigmac_rx(struct bigmac *bp)
/* Check for errors. */
if (len < ETH_ZLEN) {
- bp->enet_stats.rx_errors++;
- bp->enet_stats.rx_length_errors++;
+ bp->dev->stats.rx_errors++;
+ bp->dev->stats.rx_length_errors++;
drop_it:
/* Return it to the BigMAC. */
- bp->enet_stats.rx_dropped++;
+ bp->dev->stats.rx_dropped++;
this->rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
goto next;
@@ -875,8 +875,8 @@ static void bigmac_rx(struct bigmac *bp)
/* No checksums done by the BigMAC ;-( */
skb->protocol = eth_type_trans(skb, bp->dev);
netif_rx(skb);
- bp->enet_stats.rx_packets++;
- bp->enet_stats.rx_bytes += len;
+ bp->dev->stats.rx_packets++;
+ bp->dev->stats.rx_bytes += len;
next:
elem = NEXT_RX(elem);
this = &rxbase[elem];
@@ -987,7 +987,7 @@ static struct net_device_stats *bigmac_get_stats(struct net_device *dev)
struct bigmac *bp = netdev_priv(dev);
bigmac_get_counters(bp, bp->bregs);
- return &bp->enet_stats;
+ return &dev->stats;
}
static void bigmac_set_multicast(struct net_device *dev)
diff --git a/drivers/net/ethernet/sun/sunbmac.h b/drivers/net/ethernet/sun/sunbmac.h
index 532fc56830cf..ee56930475a8 100644
--- a/drivers/net/ethernet/sun/sunbmac.h
+++ b/drivers/net/ethernet/sun/sunbmac.h
@@ -311,7 +311,6 @@ struct bigmac {
enum bigmac_timer_state timer_state;
unsigned int timer_ticks;
- struct net_device_stats enet_stats;
struct platform_device *qec_op;
struct platform_device *bigmac_op;
struct net_device *dev;
diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c
index 53ff66ef53ac..a6cc9a2d41c1 100644
--- a/drivers/net/ethernet/sun/sunhme.c
+++ b/drivers/net/ethernet/sun/sunhme.c
@@ -933,7 +933,7 @@ static void happy_meal_stop(struct happy_meal *hp, void __iomem *gregs)
/* hp->happy_lock must be held */
static void happy_meal_get_counters(struct happy_meal *hp, void __iomem *bregs)
{
- struct net_device_stats *stats = &hp->net_stats;
+ struct net_device_stats *stats = &hp->dev->stats;
stats->rx_crc_errors += hme_read32(hp, bregs + BMAC_RCRCECTR);
hme_write32(hp, bregs + BMAC_RCRCECTR, 0);
@@ -1947,7 +1947,7 @@ static void happy_meal_tx(struct happy_meal *hp)
break;
}
hp->tx_skbs[elem] = NULL;
- hp->net_stats.tx_bytes += skb->len;
+ dev->stats.tx_bytes += skb->len;
for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
dma_addr = hme_read_desc32(hp, &this->tx_addr);
@@ -1964,7 +1964,7 @@ static void happy_meal_tx(struct happy_meal *hp)
}
dev_kfree_skb_irq(skb);
- hp->net_stats.tx_packets++;
+ dev->stats.tx_packets++;
}
hp->tx_old = elem;
TXD((">"));
@@ -2009,17 +2009,17 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev)
/* Check for errors. */
if ((len < ETH_ZLEN) || (flags & RXFLAG_OVERFLOW)) {
RXD(("ERR(%08x)]", flags));
- hp->net_stats.rx_errors++;
+ dev->stats.rx_errors++;
if (len < ETH_ZLEN)
- hp->net_stats.rx_length_errors++;
+ dev->stats.rx_length_errors++;
if (len & (RXFLAG_OVERFLOW >> 16)) {
- hp->net_stats.rx_over_errors++;
- hp->net_stats.rx_fifo_errors++;
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_fifo_errors++;
}
/* Return it to the Happy meal. */
drop_it:
- hp->net_stats.rx_dropped++;
+ dev->stats.rx_dropped++;
hme_write_rxd(hp, this,
(RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
dma_addr);
@@ -2084,8 +2084,8 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev)
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- hp->net_stats.rx_packets++;
- hp->net_stats.rx_bytes += len;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
next:
elem = NEXT_RX(elem);
this = &rxbase[elem];
@@ -2396,7 +2396,7 @@ static struct net_device_stats *happy_meal_get_stats(struct net_device *dev)
happy_meal_get_counters(hp, hp->bigmacregs);
spin_unlock_irq(&hp->happy_lock);
- return &hp->net_stats;
+ return &dev->stats;
}
static void happy_meal_set_multicast(struct net_device *dev)
diff --git a/drivers/net/ethernet/sun/sunhme.h b/drivers/net/ethernet/sun/sunhme.h
index 4a8d5b18dfd5..3af540adb3c5 100644
--- a/drivers/net/ethernet/sun/sunhme.h
+++ b/drivers/net/ethernet/sun/sunhme.h
@@ -418,8 +418,6 @@ struct happy_meal {
int rx_new, tx_new, rx_old, tx_old;
- struct net_device_stats net_stats; /* Statistical counters */
-
#if defined(CONFIG_SBUS) && defined(CONFIG_PCI)
u32 (*read32)(void __iomem *);
void (*write32)(void __iomem *, u32);
diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c
index 876f02f4945e..3d8ea18df696 100644
--- a/drivers/net/usb/kaweth.c
+++ b/drivers/net/usb/kaweth.c
@@ -245,8 +245,6 @@ struct kaweth_device
__u16 packet_filter_bitmap;
struct kaweth_ethernet_configuration configuration;
-
- struct net_device_stats stats;
};
/****************************************************************
@@ -598,7 +596,7 @@ static void kaweth_usb_receive(struct urb *urb)
struct sk_buff *skb;
if (unlikely(status == -EPIPE)) {
- kaweth->stats.rx_errors++;
+ net->stats.rx_errors++;
kaweth->end = 1;
wake_up(&kaweth->term_wait);
dev_dbg(dev, "Status was -EPIPE.\n");
@@ -613,12 +611,12 @@ static void kaweth_usb_receive(struct urb *urb)
}
if (unlikely(status == -EPROTO || status == -ETIME ||
status == -EILSEQ)) {
- kaweth->stats.rx_errors++;
+ net->stats.rx_errors++;
dev_dbg(dev, "Status was -EPROTO, -ETIME, or -EILSEQ.\n");
return;
}
if (unlikely(status == -EOVERFLOW)) {
- kaweth->stats.rx_errors++;
+ net->stats.rx_errors++;
dev_dbg(dev, "Status was -EOVERFLOW.\n");
}
spin_lock(&kaweth->device_lock);
@@ -663,8 +661,8 @@ static void kaweth_usb_receive(struct urb *urb)
netif_rx(skb);
- kaweth->stats.rx_packets++;
- kaweth->stats.rx_bytes += pkt_len;
+ net->stats.rx_packets++;
+ net->stats.rx_bytes += pkt_len;
}
kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
@@ -810,7 +808,7 @@ static netdev_tx_t kaweth_start_xmit(struct sk_buff *skb,
dev_kfree_skb_irq(skb);
skb = copied_skb;
if (!copied_skb) {
- kaweth->stats.tx_errors++;
+ net->stats.tx_errors++;
netif_start_queue(net);
spin_unlock_irq(&kaweth->device_lock);
return NETDEV_TX_OK;
@@ -834,15 +832,15 @@ static netdev_tx_t kaweth_start_xmit(struct sk_buff *skb,
{
dev_warn(&net->dev, "kaweth failed tx_urb %d\n", res);
skip:
- kaweth->stats.tx_errors++;
+ net->stats.tx_errors++;
netif_start_queue(net);
dev_kfree_skb_irq(skb);
}
else
{
- kaweth->stats.tx_packets++;
- kaweth->stats.tx_bytes += skb->len;
+ net->stats.tx_packets++;
+ net->stats.tx_bytes += skb->len;
}
spin_unlock_irq(&kaweth->device_lock);
@@ -912,15 +910,6 @@ static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth)
}
/****************************************************************
- * kaweth_netdev_stats
- ****************************************************************/
-static struct net_device_stats *kaweth_netdev_stats(struct net_device *dev)
-{
- struct kaweth_device *kaweth = netdev_priv(dev);
- return &kaweth->stats;
-}
-
-/****************************************************************
* kaweth_tx_timeout
****************************************************************/
static void kaweth_tx_timeout(struct net_device *net)
@@ -928,7 +917,7 @@ static void kaweth_tx_timeout(struct net_device *net)
struct kaweth_device *kaweth = netdev_priv(net);
dev_warn(&net->dev, "%s: Tx timed out. Resetting.\n", net->name);
- kaweth->stats.tx_errors++;
+ net->stats.tx_errors++;
netif_trans_update(net);
usb_unlink_urb(kaweth->tx_urb);
@@ -981,7 +970,6 @@ static const struct net_device_ops kaweth_netdev_ops = {
.ndo_start_xmit = kaweth_start_xmit,
.ndo_tx_timeout = kaweth_tx_timeout,
.ndo_set_rx_mode = kaweth_set_rx_mode,
- .ndo_get_stats = kaweth_netdev_stats,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c
index 321e059e13ae..6514c86f043e 100644
--- a/drivers/net/usb/pegasus.c
+++ b/drivers/net/usb/pegasus.c
@@ -501,13 +501,13 @@ static void read_bulk_callback(struct urb *urb)
if (rx_status & 0x1e) {
netif_dbg(pegasus, rx_err, net,
"RX packet error %x\n", rx_status);
- pegasus->stats.rx_errors++;
+ net->stats.rx_errors++;
if (rx_status & 0x06) /* long or runt */
- pegasus->stats.rx_length_errors++;
+ net->stats.rx_length_errors++;
if (rx_status & 0x08)
- pegasus->stats.rx_crc_errors++;
+ net->stats.rx_crc_errors++;
if (rx_status & 0x10) /* extra bits */
- pegasus->stats.rx_frame_errors++;
+ net->stats.rx_frame_errors++;
goto goon;
}
if (pegasus->chip == 0x8513) {
@@ -535,8 +535,8 @@ static void read_bulk_callback(struct urb *urb)
skb_put(pegasus->rx_skb, pkt_len);
pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net);
netif_rx(pegasus->rx_skb);
- pegasus->stats.rx_packets++;
- pegasus->stats.rx_bytes += pkt_len;
+ net->stats.rx_packets++;
+ net->stats.rx_bytes += pkt_len;
if (pegasus->flags & PEGASUS_UNPLUG)
return;
@@ -670,13 +670,13 @@ static void intr_callback(struct urb *urb)
/* byte 0 == tx_status1, reg 2B */
if (d[0] & (TX_UNDERRUN|EXCESSIVE_COL
|LATE_COL|JABBER_TIMEOUT)) {
- pegasus->stats.tx_errors++;
+ net->stats.tx_errors++;
if (d[0] & TX_UNDERRUN)
- pegasus->stats.tx_fifo_errors++;
+ net->stats.tx_fifo_errors++;
if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT))
- pegasus->stats.tx_aborted_errors++;
+ net->stats.tx_aborted_errors++;
if (d[0] & LATE_COL)
- pegasus->stats.tx_window_errors++;
+ net->stats.tx_window_errors++;
}
/* d[5].LINK_STATUS lies on some adapters.
@@ -685,7 +685,7 @@ static void intr_callback(struct urb *urb)
*/
/* bytes 3-4 == rx_lostpkt, reg 2E/2F */
- pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4];
+ net->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4];
}
res = usb_submit_urb(urb, GFP_ATOMIC);
@@ -701,7 +701,7 @@ static void pegasus_tx_timeout(struct net_device *net)
pegasus_t *pegasus = netdev_priv(net);
netif_warn(pegasus, timer, net, "tx timeout\n");
usb_unlink_urb(pegasus->tx_urb);
- pegasus->stats.tx_errors++;
+ net->stats.tx_errors++;
}
static netdev_tx_t pegasus_start_xmit(struct sk_buff *skb,
@@ -731,23 +731,18 @@ static netdev_tx_t pegasus_start_xmit(struct sk_buff *skb,
netif_device_detach(pegasus->net);
break;
default:
- pegasus->stats.tx_errors++;
+ net->stats.tx_errors++;
netif_start_queue(net);
}
} else {
- pegasus->stats.tx_packets++;
- pegasus->stats.tx_bytes += skb->len;
+ net->stats.tx_packets++;
+ net->stats.tx_bytes += skb->len;
}
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
-static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
-{
- return &((pegasus_t *) netdev_priv(dev))->stats;
-}
-
static inline void disable_net_traffic(pegasus_t *pegasus)
{
__le16 tmp = cpu_to_le16(0);
@@ -1294,7 +1289,6 @@ static const struct net_device_ops pegasus_netdev_ops = {
.ndo_do_ioctl = pegasus_ioctl,
.ndo_start_xmit = pegasus_start_xmit,
.ndo_set_rx_mode = pegasus_set_multicast,
- .ndo_get_stats = pegasus_netdev_stats,
.ndo_tx_timeout = pegasus_tx_timeout,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/usb/pegasus.h b/drivers/net/usb/pegasus.h
index d15646244fdf..9b7ea9c9167d 100644
--- a/drivers/net/usb/pegasus.h
+++ b/drivers/net/usb/pegasus.h
@@ -83,7 +83,6 @@ typedef struct pegasus {
struct usb_device *usb;
struct usb_interface *intf;
struct net_device *net;
- struct net_device_stats stats;
struct mii_if_info mii;
unsigned flags;
unsigned features;
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
index fd5944bbe224..730d9619400e 100644
--- a/drivers/s390/net/ctcm_fsms.c
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -1283,7 +1283,7 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
p_header = (struct pdu *)
(skb_tail_pointer(ch->trans_skb) - skb->len);
p_header->pdu_flag = 0x00;
- if (skb->protocol == ntohs(ETH_P_SNAP))
+ if (be16_to_cpu(skb->protocol) == ETH_P_SNAP)
p_header->pdu_flag |= 0x60;
else
p_header->pdu_flag |= 0x20;
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index ac65f12bcd43..198842ce6876 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -106,7 +106,7 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
priv->stats.rx_frame_errors++;
return;
}
- pskb->protocol = ntohs(header->type);
+ pskb->protocol = cpu_to_be16(header->type);
if ((header->length <= LL_HEADER_LENGTH) ||
(len <= LL_HEADER_LENGTH)) {
if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) {
@@ -125,7 +125,7 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
header->length -= LL_HEADER_LENGTH;
len -= LL_HEADER_LENGTH;
if ((header->length > skb_tailroom(pskb)) ||
- (header->length > len)) {
+ (header->length > len)) {
if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
"%s(%s): Packet size %d (overrun)"
@@ -485,7 +485,7 @@ static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb)
} else {
atomic_inc(&skb->users);
header.length = l;
- header.type = skb->protocol;
+ header.type = be16_to_cpu(skb->protocol);
header.unused = 0;
memcpy(skb_push(skb, LL_HEADER_LENGTH), &header,
LL_HEADER_LENGTH);
@@ -503,7 +503,7 @@ static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb)
atomic_inc(&skb->users);
ch->prof.txlen += skb->len;
header.length = skb->len + LL_HEADER_LENGTH;
- header.type = skb->protocol;
+ header.type = be16_to_cpu(skb->protocol);
header.unused = 0;
memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, LL_HEADER_LENGTH);
block_len = skb->len + 2;
@@ -690,7 +690,7 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
p_header->pdu_offset = skb->len;
p_header->pdu_proto = 0x01;
p_header->pdu_flag = 0x00;
- if (skb->protocol == ntohs(ETH_P_SNAP)) {
+ if (be16_to_cpu(skb->protocol) == ETH_P_SNAP) {
p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;
} else {
p_header->pdu_flag |= PDU_FIRST;
@@ -745,7 +745,7 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
p_header->pdu_proto = 0x01;
p_header->pdu_flag = 0x00;
p_header->pdu_seq = 0;
- if (skb->protocol == ntohs(ETH_P_SNAP)) {
+ if (be16_to_cpu(skb->protocol) == ETH_P_SNAP) {
p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;
} else {
p_header->pdu_flag |= PDU_FIRST;
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index 3f85b97ab8d2..dba94b486f05 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -635,7 +635,7 @@ static void netiucv_unpack_skb(struct iucv_connection *conn,
skb_put(pskb, NETIUCV_HDRLEN);
pskb->dev = dev;
pskb->ip_summed = CHECKSUM_NONE;
- pskb->protocol = ntohs(ETH_P_IP);
+ pskb->protocol = cpu_to_be16(ETH_P_IP);
while (1) {
struct sk_buff *skb;
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index d9561e39c3b2..ed5b3582adba 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -503,22 +503,12 @@ struct qeth_qdio_info {
int default_out_queue;
};
-enum qeth_send_errors {
- QETH_SEND_ERROR_NONE,
- QETH_SEND_ERROR_LINK_FAILURE,
- QETH_SEND_ERROR_RETRY,
- QETH_SEND_ERROR_KICK_IT,
-};
-
#define QETH_ETH_MAC_V4 0x0100 /* like v4 */
#define QETH_ETH_MAC_V6 0x3333 /* like v6 */
/* tr mc mac is longer, but that will be enough to detect mc frames */
#define QETH_TR_MAC_NC 0xc000 /* non-canonical */
#define QETH_TR_MAC_C 0x0300 /* canonical */
-#define DEFAULT_ADD_HHLEN 0
-#define MAX_ADD_HHLEN 1024
-
/**
* buffer stuff for read channel
*/
@@ -644,7 +634,6 @@ struct qeth_reply {
atomic_t refcnt;
};
-
struct qeth_card_blkt {
int time_total;
int inter_packet;
@@ -685,7 +674,6 @@ struct qeth_card_options {
struct qeth_ipa_info ipa6;
struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */
int fake_broadcast;
- int add_hhlen;
int layer2;
int performance_stats;
int rx_sg_cb;
@@ -856,9 +844,9 @@ static inline int qeth_get_ip_version(struct sk_buff *skb)
{
__be16 *p = &((struct ethhdr *)skb->data)->h_proto;
- if (*p == ETH_P_8021Q)
+ if (be16_to_cpu(*p) == ETH_P_8021Q)
p += 2;
- switch (*p) {
+ switch (be16_to_cpu(*p)) {
case ETH_P_IPV6:
return 6;
case ETH_P_IP:
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 9a5f99ccb122..6b22b05a6953 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -55,7 +55,6 @@ static struct mutex qeth_mod_mutex;
static void qeth_send_control_data_cb(struct qeth_channel *,
struct qeth_cmd_buffer *);
-static int qeth_issue_next_read(struct qeth_card *);
static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *);
static void qeth_setup_ccw(struct qeth_channel *, unsigned char *, __u32);
static void qeth_free_buffer_pool(struct qeth_card *);
@@ -1202,7 +1201,7 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *q,
while (skb) {
QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification);
QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb);
- if (skb->protocol == ETH_P_AF_IUCV) {
+ if (be16_to_cpu(skb->protocol) == ETH_P_AF_IUCV) {
if (skb->sk) {
struct iucv_sock *iucv = iucv_sk(skb->sk);
iucv->sk_txnotify(skb, notification);
@@ -1233,7 +1232,8 @@ static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf)
while (skb) {
QETH_CARD_TEXT(buf->q->card, 5, "skbr");
QETH_CARD_TEXT_(buf->q->card, 5, "%lx", (long) skb);
- if (notify_general_error && skb->protocol == ETH_P_AF_IUCV) {
+ if (notify_general_error &&
+ be16_to_cpu(skb->protocol) == ETH_P_AF_IUCV) {
if (skb->sk) {
iucv = iucv_sk(skb->sk);
iucv->sk_txnotify(skb, TX_NOTIFY_GENERALERROR);
@@ -1396,7 +1396,6 @@ static void qeth_set_intial_options(struct qeth_card *card)
card->options.route4.type = NO_ROUTER;
card->options.route6.type = NO_ROUTER;
card->options.fake_broadcast = 0;
- card->options.add_hhlen = DEFAULT_ADD_HHLEN;
card->options.performance_stats = 0;
card->options.rx_sg_cb = QETH_RX_SG_CB;
card->options.isolation = ISOLATION_MODE_NONE;
@@ -3322,7 +3321,7 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)
}
EXPORT_SYMBOL_GPL(qeth_queue_input_buffer);
-static int qeth_handle_send_error(struct qeth_card *card,
+static void qeth_handle_send_error(struct qeth_card *card,
struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err)
{
int sbalf15 = buffer->buffer->element[15].sflags;
@@ -3338,15 +3337,14 @@ static int qeth_handle_send_error(struct qeth_card *card,
qeth_check_qdio_errors(card, buffer->buffer, qdio_err, "qouterr");
if (!qdio_err)
- return QETH_SEND_ERROR_NONE;
+ return;
if ((sbalf15 >= 15) && (sbalf15 <= 31))
- return QETH_SEND_ERROR_RETRY;
+ return;
QETH_CARD_TEXT(card, 1, "lnkfail");
QETH_CARD_TEXT_(card, 1, "%04x %02x",
(u16)qdio_err, (u8)sbalf15);
- return QETH_SEND_ERROR_LINK_FAILURE;
}
/*
@@ -3799,9 +3797,9 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
return qeth_cut_iqd_prio(card, ~skb->priority >> 1 & 3);
case QETH_PRIO_Q_ING_VLAN:
tci = &((struct ethhdr *)skb->data)->h_proto;
- if (*tci == ETH_P_8021Q)
- return qeth_cut_iqd_prio(card, ~*(tci + 1) >>
- (VLAN_PRIO_SHIFT + 1) & 3);
+ if (be16_to_cpu(*tci) == ETH_P_8021Q)
+ return qeth_cut_iqd_prio(card,
+ ~be16_to_cpu(*(tci + 1)) >> (VLAN_PRIO_SHIFT + 1) & 3);
break;
default:
break;
@@ -4775,12 +4773,10 @@ static int qeth_query_card_info(struct qeth_card *card,
static inline int qeth_get_qdio_q_format(struct qeth_card *card)
{
- switch (card->info.type) {
- case QETH_CARD_TYPE_IQD:
- return 2;
- default:
- return 0;
- }
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ return QDIO_IQDIO_QFMT;
+ else
+ return QDIO_QETH_QFMT;
}
static void qeth_determine_capabilities(struct qeth_card *card)
@@ -4819,8 +4815,9 @@ static void qeth_determine_capabilities(struct qeth_card *card)
QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
QETH_DBF_TEXT_(SETUP, 2, "qfmt%d", card->ssqd.qfmt);
- QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac1);
- QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac3);
+ QETH_DBF_TEXT_(SETUP, 2, "ac1:%02x", card->ssqd.qdioac1);
+ QETH_DBF_TEXT_(SETUP, 2, "ac2:%04x", card->ssqd.qdioac2);
+ QETH_DBF_TEXT_(SETUP, 2, "ac3:%04x", card->ssqd.qdioac3);
QETH_DBF_TEXT_(SETUP, 2, "icnt%d", card->ssqd.icnt);
if (!((card->ssqd.qfmt != QDIO_IQDIO_QFMT) ||
((card->ssqd.qdioac1 & CHSC_AC1_INITIATE_INPUTQ) == 0) ||
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h
index bc69d0a338ad..4accb0a61ce0 100644
--- a/drivers/s390/net/qeth_core_mpc.h
+++ b/drivers/s390/net/qeth_core_mpc.h
@@ -29,7 +29,6 @@ extern unsigned char IPA_PDU_HEADER[];
#define QETH_TIMEOUT (10 * HZ)
#define QETH_IPA_TIMEOUT (45 * HZ)
#define QETH_IDX_COMMAND_SEQNO 0xffff0000
-#define SR_INFO_LEN 16
#define QETH_CLEAR_CHANNEL_PARM -10
#define QETH_HALT_CHANNEL_PARM -11
@@ -65,7 +64,6 @@ enum qeth_link_types {
QETH_LINK_TYPE_LANE_TR = 0x82,
QETH_LINK_TYPE_LANE_ETH1000 = 0x83,
QETH_LINK_TYPE_LANE = 0x88,
- QETH_LINK_TYPE_ATM_NATIVE = 0x90,
};
/*
@@ -185,8 +183,6 @@ enum qeth_ipa_return_codes {
IPA_RC_ENOMEM = 0xfffe,
IPA_RC_FFFF = 0xffff
};
-/* for DELIP */
-#define IPA_RC_IP_ADDRESS_NOT_DEFINED IPA_RC_PRIMARY_ALREADY_DEFINED
/* for SET_DIAGNOSTIC_ASSIST */
#define IPA_RC_INVALID_SUBCMD IPA_RC_IP_TABLE_FULL
#define IPA_RC_HARDWARE_AUTH_ERROR IPA_RC_UNKNOWN_ERROR
@@ -631,14 +627,6 @@ enum qeth_ipa_addr_change_code {
IPA_ADDR_CHANGE_CODE_MACADDR = 0x02,
IPA_ADDR_CHANGE_CODE_REMOVAL = 0x80, /* else addition */
};
-enum qeth_ipa_addr_change_retcode {
- IPA_ADDR_CHANGE_RETCODE_OK = 0x0000,
- IPA_ADDR_CHANGE_RETCODE_LOSTEVENTS = 0x0010,
-};
-enum qeth_ipa_addr_change_lostmask {
- IPA_ADDR_CHANGE_MASK_OVERFLOW = 0x01,
- IPA_ADDR_CHANGE_MASK_STATECHANGE = 0x02,
-};
struct qeth_ipacmd_addr_change_entry {
struct net_if_token token;
@@ -817,9 +805,4 @@ extern unsigned char IDX_ACTIVATE_WRITE[];
((buffer) && \
(*(buffer + ((*(buffer + 0x0b)) + 4)) == 0xc1))
-#define ADDR_FRAME_TYPE_DIX 1
-#define ADDR_FRAME_TYPE_802_3 2
-#define ADDR_FRAME_TYPE_TR_WITHOUT_SR 0x10
-#define ADDR_FRAME_TYPE_TR_WITH_SR 0x20
-
#endif
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index af4e6a639fec..cd0ba9e0d3e5 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -28,7 +28,6 @@
static int qeth_l2_set_offline(struct ccwgroup_device *);
static int qeth_l2_stop(struct net_device *);
static void qeth_l2_set_rx_mode(struct net_device *);
-static int qeth_l2_recover(void *);
static void qeth_bridgeport_query_support(struct qeth_card *card);
static void qeth_bridge_state_change(struct qeth_card *card,
struct qeth_ipa_cmd *cmd);
diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c
index 692db49e3d2a..687972356d6b 100644
--- a/drivers/s390/net/qeth_l2_sys.c
+++ b/drivers/s390/net/qeth_l2_sys.c
@@ -8,9 +8,6 @@
#include "qeth_core.h"
#include "qeth_l2.h"
-#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \
-struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store)
-
static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
struct device_attribute *attr, char *buf,
int show_state)
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 653f0fb76573..c29525838b7c 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -36,16 +36,12 @@
static int qeth_l3_set_offline(struct ccwgroup_device *);
-static int qeth_l3_recover(void *);
static int qeth_l3_stop(struct net_device *);
static void qeth_l3_set_multicast_list(struct net_device *);
-static int qeth_l3_neigh_setup(struct net_device *, struct neigh_parms *);
static int qeth_l3_register_addr_entry(struct qeth_card *,
struct qeth_ipaddr *);
static int qeth_l3_deregister_addr_entry(struct qeth_card *,
struct qeth_ipaddr *);
-static int __qeth_l3_set_online(struct ccwgroup_device *, int);
-static int __qeth_l3_set_offline(struct ccwgroup_device *, int);
static int qeth_l3_isxdigit(char *buf)
{
@@ -1341,7 +1337,7 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd)
return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL);
}
-static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac)
+static void qeth_l3_get_mac_for_ipm(__be32 ipm, char *mac)
{
ip_eth_mc_map(ipm, mac);
}
@@ -1414,7 +1410,7 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev)
im4 = rcu_dereference(im4->next_rcu)) {
qeth_l3_get_mac_for_ipm(im4->multiaddr, buf);
- tmp->u.a4.addr = im4->multiaddr;
+ tmp->u.a4.addr = be32_to_cpu(im4->multiaddr);
memcpy(tmp->mac, buf, sizeof(tmp->mac));
ipm = qeth_l3_ip_from_hash(card, tmp);
@@ -1425,7 +1421,7 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev)
if (!ipm)
continue;
memcpy(ipm->mac, buf, sizeof(tmp->mac));
- ipm->u.a4.addr = im4->multiaddr;
+ ipm->u.a4.addr = be32_to_cpu(im4->multiaddr);
ipm->is_multicast = 1;
ipm->disp_flag = QETH_DISP_ADDR_ADD;
hash_add(card->ip_mc_htable,
@@ -1598,8 +1594,8 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card,
spin_lock_bh(&card->ip_lock);
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
- addr->u.a4.addr = ifa->ifa_address;
- addr->u.a4.mask = ifa->ifa_mask;
+ addr->u.a4.addr = be32_to_cpu(ifa->ifa_address);
+ addr->u.a4.mask = be32_to_cpu(ifa->ifa_mask);
addr->type = QETH_IP_TYPE_NORMAL;
qeth_l3_delete_ip(card, addr);
}
@@ -1690,25 +1686,25 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card,
struct sk_buff *skb, struct qeth_hdr *hdr,
unsigned short *vlan_id)
{
- __be16 prot;
+ __u16 prot;
struct iphdr *ip_hdr;
unsigned char tg_addr[MAX_ADDR_LEN];
int is_vlan = 0;
if (!(hdr->hdr.l3.flags & QETH_HDR_PASSTHRU)) {
- prot = htons((hdr->hdr.l3.flags & QETH_HDR_IPV6)? ETH_P_IPV6 :
- ETH_P_IP);
+ prot = (hdr->hdr.l3.flags & QETH_HDR_IPV6) ? ETH_P_IPV6 :
+ ETH_P_IP;
switch (hdr->hdr.l3.flags & QETH_HDR_CAST_MASK) {
case QETH_CAST_MULTICAST:
switch (prot) {
#ifdef CONFIG_QETH_IPV6
- case __constant_htons(ETH_P_IPV6):
+ case ETH_P_IPV6:
ndisc_mc_map((struct in6_addr *)
skb->data + 24,
tg_addr, card->dev, 0);
break;
#endif
- case __constant_htons(ETH_P_IP):
+ case ETH_P_IP:
ip_hdr = (struct iphdr *)skb->data;
ip_eth_mc_map(ip_hdr->daddr, tg_addr);
break;
@@ -1795,7 +1791,7 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
magic = *(__u16 *)skb->data;
if ((card->info.type == QETH_CARD_TYPE_IQD) &&
(magic == ETH_P_AF_IUCV)) {
- skb->protocol = ETH_P_AF_IUCV;
+ skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
skb->pkt_type = PACKET_HOST;
skb->mac_header = NET_SKB_PAD;
skb->dev = card->dev;
@@ -2572,10 +2568,10 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
rcu_read_unlock();
/* try something else */
- if (skb->protocol == ETH_P_IPV6)
+ if (be16_to_cpu(skb->protocol) == ETH_P_IPV6)
return (skb_network_header(skb)[24] == 0xff) ?
RTN_MULTICAST : 0;
- else if (skb->protocol == ETH_P_IP)
+ else if (be16_to_cpu(skb->protocol) == ETH_P_IP)
return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ?
RTN_MULTICAST : 0;
/* ... */
@@ -2726,7 +2722,7 @@ static void qeth_tso_fill_header(struct qeth_card *card,
hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
sizeof(struct qeth_hdr_tso));
tcph->check = 0;
- if (skb->protocol == ETH_P_IPV6) {
+ if (be16_to_cpu(skb->protocol) == ETH_P_IPV6) {
ip6h->payload_len = 0;
tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
0, IPPROTO_TCP, 0);
@@ -2773,7 +2769,7 @@ static int qeth_l3_get_elements_no_tso(struct qeth_card *card,
static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
int rc;
- u16 *tag;
+ __be16 *tag;
struct qeth_hdr *hdr = NULL;
int hdr_elements = 0;
int elements;
@@ -2794,7 +2790,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (((card->info.type == QETH_CARD_TYPE_IQD) &&
(((card->options.cq != QETH_CQ_ENABLED) && !ipv) ||
((card->options.cq == QETH_CQ_ENABLED) &&
- (skb->protocol != ETH_P_AF_IUCV)))) ||
+ (be16_to_cpu(skb->protocol) != ETH_P_AF_IUCV)))) ||
card->options.sniffer)
goto tx_drop;
@@ -2847,9 +2843,9 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
new_skb->data + 8, 4);
skb_copy_to_linear_data_offset(new_skb, 8,
new_skb->data + 12, 4);
- tag = (u16 *)(new_skb->data + 12);
- *tag = __constant_htons(ETH_P_8021Q);
- *(tag + 1) = htons(skb_vlan_tag_get(new_skb));
+ tag = (__be16 *)(new_skb->data + 12);
+ *tag = cpu_to_be16(ETH_P_8021Q);
+ *(tag + 1) = cpu_to_be16(skb_vlan_tag_get(new_skb));
}
}
@@ -2887,7 +2883,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
qeth_l3_fill_header(card, hdr, new_skb, ipv,
cast_type);
} else {
- if (new_skb->protocol == ETH_P_AF_IUCV)
+ if (be16_to_cpu(new_skb->protocol) == ETH_P_AF_IUCV)
qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb);
else {
qeth_l3_fill_header(card, hdr, new_skb, ipv,
@@ -3466,8 +3462,8 @@ static int qeth_l3_ip_event(struct notifier_block *this,
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
if (addr) {
- addr->u.a4.addr = ifa->ifa_address;
- addr->u.a4.mask = ifa->ifa_mask;
+ addr->u.a4.addr = be32_to_cpu(ifa->ifa_address);
+ addr->u.a4.mask = be32_to_cpu(ifa->ifa_mask);
addr->type = QETH_IP_TYPE_NORMAL;
} else
return NOTIFY_DONE;
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
index 05e9471e3d3f..ff29a4b416b4 100644
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -286,7 +286,7 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
if (!addr)
return -ENOMEM;
- addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
+ addr->u.a6.addr.s6_addr32[0] = cpu_to_be32(0xfe800000);
addr->u.a6.addr.s6_addr32[1] = 0x00000000;
for (i = 8; i < 16; i++)
addr->u.a6.addr.s6_addr[i] =
@@ -320,7 +320,7 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
if (addr != NULL) {
- addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
+ addr->u.a6.addr.s6_addr32[0] = cpu_to_be32(0xfe800000);
addr->u.a6.addr.s6_addr32[1] = 0x00000000;
for (i = 8; i < 16; i++)
addr->u.a6.addr.s6_addr[i] = card->options.hsuid[i - 8];
diff --git a/include/net/dsa.h b/include/net/dsa.h
index ffe56cc338fe..7ba9b1fb565c 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -32,6 +32,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_EDSA,
DSA_TAG_PROTO_BRCM,
DSA_TAG_PROTO_QCA,
+ DSA_TAG_PROTO_MTK,
DSA_TAG_LAST, /* MUST BE LAST */
};
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index da4d64f432db..aa21f49f1215 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -31,4 +31,6 @@ config NET_DSA_TAG_TRAILER
config NET_DSA_TAG_QCA
bool
+config NET_DSA_TAG_MTK
+ bool
endif
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 31d343796251..9b1d478f3713 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -8,3 +8,4 @@ dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
dsa_core-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
+dsa_core-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 95d1a756202c..6cad15da5892 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -54,6 +54,9 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = {
#ifdef CONFIG_NET_DSA_TAG_QCA
[DSA_TAG_PROTO_QCA] = &qca_netdev_ops,
#endif
+#ifdef CONFIG_NET_DSA_TAG_MTK
+ [DSA_TAG_PROTO_MTK] = &mtk_netdev_ops,
+#endif
[DSA_TAG_PROTO_NONE] = &none_ops,
};
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 0706a511244e..2a3139921811 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -85,4 +85,7 @@ extern const struct dsa_device_ops brcm_netdev_ops;
/* tag_qca.c */
extern const struct dsa_device_ops qca_netdev_ops;
+/* tag_mtk.c */
+extern const struct dsa_device_ops mtk_netdev_ops;
+
#endif
diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c
new file mode 100644
index 000000000000..44ae6353a521
--- /dev/null
+++ b/net/dsa/tag_mtk.c
@@ -0,0 +1,118 @@
+/*
+ * Mediatek DSA Tag support
+ * Copyright (C) 2017 Landen Chao <[email protected]>
+ * Sean Wang <[email protected]>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/etherdevice.h>
+#include <net/dsa.h>
+#include "dsa_priv.h"
+
+#define MTK_HDR_LEN 4
+#define MTK_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0)
+#define MTK_HDR_XMIT_DP_BIT_MASK GENMASK(5, 0)
+
+static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct dsa_slave_priv *p = netdev_priv(dev);
+ u8 *mtk_tag;
+
+ if (skb_cow_head(skb, MTK_HDR_LEN) < 0)
+ goto out_free;
+
+ skb_push(skb, MTK_HDR_LEN);
+
+ memmove(skb->data, skb->data + MTK_HDR_LEN, 2 * ETH_ALEN);
+
+ /* Build the tag after the MAC Source Address */
+ mtk_tag = skb->data + 2 * ETH_ALEN;
+ mtk_tag[0] = 0;
+ mtk_tag[1] = (1 << p->dp->index) & MTK_HDR_XMIT_DP_BIT_MASK;
+ mtk_tag[2] = 0;
+ mtk_tag[3] = 0;
+
+ return skb;
+
+out_free:
+ kfree_skb(skb);
+ return NULL;
+}
+
+static int mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dsa_switch_tree *dst = dev->dsa_ptr;
+ struct dsa_switch *ds;
+ int port;
+ __be16 *phdr, hdr;
+
+ if (unlikely(!dst))
+ goto out_drop;
+
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb)
+ goto out;
+
+ if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN)))
+ goto out_drop;
+
+ /* The MTK header is added by the switch between src addr
+ * and ethertype at this point, skb->data points to 2 bytes
+ * after src addr so header should be 2 bytes right before.
+ */
+ phdr = (__be16 *)(skb->data - 2);
+ hdr = ntohs(*phdr);
+
+ /* Remove MTK tag and recalculate checksum. */
+ skb_pull_rcsum(skb, MTK_HDR_LEN);
+
+ memmove(skb->data - ETH_HLEN,
+ skb->data - ETH_HLEN - MTK_HDR_LEN,
+ 2 * ETH_ALEN);
+
+ /* This protocol doesn't support cascading multiple
+ * switches so it's safe to assume the switch is first
+ * in the tree.
+ */
+ ds = dst->ds[0];
+ if (!ds)
+ goto out_drop;
+
+ /* Get source port information */
+ port = (hdr & MTK_HDR_RECV_SOURCE_PORT_MASK);
+ if (!ds->ports[port].netdev)
+ goto out_drop;
+
+ /* Update skb & forward the frame accordingly */
+ skb_push(skb, ETH_HLEN);
+
+ skb->pkt_type = PACKET_HOST;
+ skb->dev = ds->ports[port].netdev;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ skb->dev->stats.rx_packets++;
+ skb->dev->stats.rx_bytes += skb->len;
+
+ netif_receive_skb(skb);
+
+ return 0;
+
+out_drop:
+ kfree_skb(skb);
+out:
+ return 0;
+}
+
+const struct dsa_device_ops mtk_netdev_ops = {
+ .xmit = mtk_tag_xmit,
+ .rcv = mtk_tag_rcv,
+};
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 5dda1ef81c7e..5e1e60546fce 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -2667,10 +2667,6 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
- /* Bugfix: need to give ip_route_input enough of an IP header to not gag. */
- ip_hdr(skb)->protocol = IPPROTO_ICMP;
- skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
-
src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0;
dst = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0;
iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0;
@@ -2680,6 +2676,15 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
else
uid = (iif ? INVALID_UID : current_uid());
+ /* Bugfix: need to give ip_route_input enough of an IP header to
+ * not gag.
+ */
+ ip_hdr(skb)->protocol = IPPROTO_UDP;
+ ip_hdr(skb)->saddr = src;
+ ip_hdr(skb)->daddr = dst;
+
+ skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
+
memset(&fl4, 0, sizeof(fl4));
fl4.daddr = dst;
fl4.saddr = src;