* [PATCH v7 1/3] dt-bindings: vendor-prefixes: Add asix prefix
From: Łukasz Stelmach @ 2020-11-24 12:03 UTC (permalink / raw)
To: Andrew Lunn, jim.cromie, Heiner Kallweit, David S. Miller,
Jakub Kicinski, Rob Herring, Kukjin Kim, Krzysztof Kozlowski,
Russell King, netdev, devicetree, linux-kernel, linux-arm-kernel,
linux-samsung-soc
Cc: Bartłomiej Żolnierkiewicz, Marek Szyprowski,
Łukasz Stelmach, Rob Herring
In-Reply-To: <20201124120330.32445-1-l.stelmach@samsung.com>
Add the prefix for ASIX Electronics Corporation.
Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 2735be1a8470..ce3b3f6c9728 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -117,6 +117,8 @@ patternProperties:
description: Asahi Kasei Corp.
"^asc,.*":
description: All Sensors Corporation
+ "^asix,.*":
+ description: ASIX Electronics Corporation
"^aspeed,.*":
description: ASPEED Technology Inc.
"^asus,.*":
--
2.26.2
^ permalink raw reply related
* [PATCH v7 3/3] net: ax88796c: ASIX AX88796C SPI Ethernet Adapter Driver
From: Łukasz Stelmach @ 2020-11-24 12:03 UTC (permalink / raw)
To: Andrew Lunn, jim.cromie, Heiner Kallweit, David S. Miller,
Jakub Kicinski, Rob Herring, Kukjin Kim, Krzysztof Kozlowski,
Russell King, netdev, devicetree, linux-kernel, linux-arm-kernel,
linux-samsung-soc
Cc: Bartłomiej Żolnierkiewicz, Marek Szyprowski,
Łukasz Stelmach
In-Reply-To: <20201124120330.32445-1-l.stelmach@samsung.com>
ASIX AX88796[1] is a versatile ethernet adapter chip, that can be
connected to a CPU with a 8/16-bit bus or with an SPI. This driver
supports SPI connection.
The driver has been ported from the vendor kernel for ARTIK5[2]
boards. Several changes were made to adapt it to the current kernel
which include:
+ updated DT configuration,
+ clock configuration moved to DT,
+ new timer, ethtool and gpio APIs,
+ dev_* instead of pr_* and custom printk() wrappers,
+ removed awkward vendor power managemtn.
+ introduced ethtool tunable to control SPI compression
[1] https://www.asix.com.tw/products.php?op=pItemdetail&PItemID=104;65;86&PLine=65
[2] https://git.tizen.org/cgit/profile/common/platform/kernel/linux-3.10-artik/
The other ax88796 driver is for NE2000 compatible AX88796L chip. These
chips are not compatible. Hence, two separate drivers are required.
Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
---
MAINTAINERS | 6 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/asix/Kconfig | 35 +
drivers/net/ethernet/asix/Makefile | 6 +
drivers/net/ethernet/asix/ax88796c_ioctl.c | 221 ++++
drivers/net/ethernet/asix/ax88796c_ioctl.h | 26 +
drivers/net/ethernet/asix/ax88796c_main.c | 1132 ++++++++++++++++++++
drivers/net/ethernet/asix/ax88796c_main.h | 561 ++++++++++
drivers/net/ethernet/asix/ax88796c_spi.c | 112 ++
drivers/net/ethernet/asix/ax88796c_spi.h | 69 ++
include/uapi/linux/ethtool.h | 1 +
net/ethtool/common.c | 1 +
13 files changed, 2172 insertions(+)
create mode 100644 drivers/net/ethernet/asix/Kconfig
create mode 100644 drivers/net/ethernet/asix/Makefile
create mode 100644 drivers/net/ethernet/asix/ax88796c_ioctl.c
create mode 100644 drivers/net/ethernet/asix/ax88796c_ioctl.h
create mode 100644 drivers/net/ethernet/asix/ax88796c_main.c
create mode 100644 drivers/net/ethernet/asix/ax88796c_main.h
create mode 100644 drivers/net/ethernet/asix/ax88796c_spi.c
create mode 100644 drivers/net/ethernet/asix/ax88796c_spi.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 14b8ec0bb58b..930dc859d4f7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2812,6 +2812,12 @@ S: Maintained
F: Documentation/hwmon/asc7621.rst
F: drivers/hwmon/asc7621.c
+ASIX AX88796C SPI ETHERNET ADAPTER
+M: Łukasz Stelmach <l.stelmach@samsung.com>
+S: Maintained
+F: Documentation/devicetree/bindings/net/asix,ax99706c-spi.yaml
+F: drivers/net/ethernet/asix/ax88796c_*
+
ASPEED PINCTRL DRIVERS
M: Andrew Jeffery <andrew@aj.id.au>
L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index de50e8b9e656..f3b218e45ea5 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -32,6 +32,7 @@ source "drivers/net/ethernet/apm/Kconfig"
source "drivers/net/ethernet/apple/Kconfig"
source "drivers/net/ethernet/aquantia/Kconfig"
source "drivers/net/ethernet/arc/Kconfig"
+source "drivers/net/ethernet/asix/Kconfig"
source "drivers/net/ethernet/atheros/Kconfig"
source "drivers/net/ethernet/aurora/Kconfig"
source "drivers/net/ethernet/broadcom/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index f8f38dcb5f8a..9eb368d93607 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_NET_XGENE) += apm/
obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
obj-$(CONFIG_NET_VENDOR_AQUANTIA) += aquantia/
obj-$(CONFIG_NET_VENDOR_ARC) += arc/
+obj-$(CONFIG_NET_VENDOR_ASIX) += asix/
obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
obj-$(CONFIG_NET_VENDOR_AURORA) += aurora/
obj-$(CONFIG_NET_VENDOR_CADENCE) += cadence/
diff --git a/drivers/net/ethernet/asix/Kconfig b/drivers/net/ethernet/asix/Kconfig
new file mode 100644
index 000000000000..eed02453314c
--- /dev/null
+++ b/drivers/net/ethernet/asix/Kconfig
@@ -0,0 +1,35 @@
+#
+# Asix network device configuration
+#
+
+config NET_VENDOR_ASIX
+ bool "Asix devices"
+ default y
+ help
+ If you have a network (Ethernet, non-USB, not NE2000 compatible)
+ interface based on a chip from ASIX, say Y.
+
+if NET_VENDOR_ASIX
+
+config SPI_AX88796C
+ tristate "Asix AX88796C-SPI support"
+ select PHYLIB
+ depends on SPI
+ depends on GPIOLIB
+ help
+ Say Y here if you intend to use ASIX AX88796C attached in SPI mode.
+
+config SPI_AX88796C_COMPRESSION
+ bool "SPI transfer compression"
+ default n
+ depends on SPI_AX88796C
+ help
+ Say Y here to enable SPI transfer compression. It saves up
+ to 24 dummy cycles during each transfer which may noticeably
+ speed up short transfers. This sets the default value that is
+ inherited by network interfaces during probe. It can be
+ changed at run time via spi-compression ethtool tunable.
+
+ If unsure say N.
+
+endif # NET_VENDOR_ASIX
diff --git a/drivers/net/ethernet/asix/Makefile b/drivers/net/ethernet/asix/Makefile
new file mode 100644
index 000000000000..0bfbbb042634
--- /dev/null
+++ b/drivers/net/ethernet/asix/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Asix network device drivers.
+#
+
+obj-$(CONFIG_SPI_AX88796C) += ax88796c.o
+ax88796c-y := ax88796c_main.o ax88796c_ioctl.o ax88796c_spi.o
diff --git a/drivers/net/ethernet/asix/ax88796c_ioctl.c b/drivers/net/ethernet/asix/ax88796c_ioctl.c
new file mode 100644
index 000000000000..941c2ff2c766
--- /dev/null
+++ b/drivers/net/ethernet/asix/ax88796c_ioctl.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2010 ASIX Electronics Corporation
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * ASIX AX88796C SPI Fast Ethernet Linux driver
+ */
+
+#define pr_fmt(fmt) "ax88796c: " fmt
+
+#include <linux/bitmap.h>
+#include <linux/iopoll.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+
+#include "ax88796c_main.h"
+#include "ax88796c_ioctl.h"
+
+static void
+ax88796c_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+ /* Inherit standard device info */
+ strncpy(info->driver, DRV_NAME, sizeof(info->driver));
+}
+
+static u32 ax88796c_get_msglevel(struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+
+ return ax_local->msg_enable;
+}
+
+static void ax88796c_set_msglevel(struct net_device *ndev, u32 level)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+
+ ax_local->msg_enable = level;
+}
+
+static void
+ax88796c_get_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pause)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+
+ pause->tx_pause = !!(ax_local->flowctrl & AX_FC_TX);
+ pause->rx_pause = !!(ax_local->flowctrl & AX_FC_RX);
+ pause->autoneg = (ax_local->flowctrl & AX_FC_ANEG) ?
+ AUTONEG_ENABLE :
+ AUTONEG_DISABLE;
+}
+
+static int
+ax88796c_set_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pause)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+ int fc;
+
+ /* The following logic comes from phylink_ethtool_set_pauseparam() */
+ fc = pause->tx_pause ? AX_FC_TX : 0;
+ fc |= pause->rx_pause ? AX_FC_RX : 0;
+ fc |= pause->autoneg ? AX_FC_ANEG : 0;
+
+ ax_local->flowctrl = fc;
+
+ if (pause->autoneg) {
+ phy_set_asym_pause(ax_local->phydev, pause->tx_pause,
+ pause->rx_pause);
+ } else {
+ int maccr = 0;
+
+ phy_set_asym_pause(ax_local->phydev, 0, 0);
+ maccr |= (ax_local->flowctrl & AX_FC_RX) ? MACCR_RXFC_ENABLE : 0;
+ maccr |= (ax_local->flowctrl & AX_FC_TX) ? MACCR_TXFC_ENABLE : 0;
+
+ mutex_lock(&ax_local->spi_lock);
+
+ maccr |= AX_READ(&ax_local->ax_spi, P0_MACCR) &
+ ~(MACCR_TXFC_ENABLE | MACCR_RXFC_ENABLE);
+ AX_WRITE(&ax_local->ax_spi, maccr, P0_MACCR);
+
+ mutex_unlock(&ax_local->spi_lock);
+ }
+
+ return 0;
+}
+
+static int ax88796c_get_regs_len(struct net_device *ndev)
+{
+ return AX88796C_REGDUMP_LEN + AX88796C_PHY_REGDUMP_LEN;
+}
+
+static void
+ax88796c_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *_p)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+ int offset, i;
+ u16 *p = _p;
+
+ memset(p, 0, ax88796c_get_regs_len(ndev));
+
+ mutex_lock(&ax_local->spi_lock);
+
+ for (offset = 0; offset < AX88796C_REGDUMP_LEN; offset += 2) {
+ if (!test_bit(offset / 2, ax88796c_no_regs_mask))
+ *p = AX_READ(&ax_local->ax_spi, offset);
+ p++;
+ }
+
+ mutex_unlock(&ax_local->spi_lock);
+
+ for (i = 0; i < AX88796C_PHY_REGDUMP_LEN / 2; i++) {
+ *p = phy_read(ax_local->phydev, i);
+ p++;
+ }
+}
+
+static int
+ax88796c_set_tunable(struct net_device *ndev, const struct ethtool_tunable *tuna,
+ const void *data)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+
+ switch (tuna->id) {
+ case ETHTOOL_SPI_COMPRESSION:
+ if (netif_running(ndev))
+ return -EBUSY;
+ if ((*(u32 *)data) != 1)
+ return -EINVAL;
+ ax_local->capabilities &= ~AX_CAP_COMP;
+ ax_local->capabilities |= (*(u32 *)data) == 1 ? AX_CAP_COMP : 0;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int
+ax88796c_get_tunable(struct net_device *ndev, const struct ethtool_tunable *tuna,
+ void *data)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+
+ switch (tuna->id) {
+ case ETHTOOL_SPI_COMPRESSION:
+ *(u32 *)data = !!(ax_local->ax_spi.comp & AX_CAP_COMP);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+int ax88796c_mdio_read(struct mii_bus *mdiobus, int phy_id, int loc)
+{
+ struct ax88796c_device *ax_local = mdiobus->priv;
+ int ret;
+
+ mutex_lock(&ax_local->spi_lock);
+ AX_WRITE(&ax_local->ax_spi, MDIOCR_RADDR(loc)
+ | MDIOCR_FADDR(phy_id) | MDIOCR_READ, P2_MDIOCR);
+
+ ret = read_poll_timeout(AX_READ, ret,
+ (ret != 0),
+ 0, jiffies_to_usecs(HZ / 100), false,
+ &ax_local->ax_spi, P2_MDIOCR);
+ if (!ret)
+ ret = AX_READ(&ax_local->ax_spi, P2_MDIODR);
+
+ mutex_unlock(&ax_local->spi_lock);
+
+ return ret;
+}
+
+int
+ax88796c_mdio_write(struct mii_bus *mdiobus, int phy_id, int loc, u16 val)
+{
+ struct ax88796c_device *ax_local = mdiobus->priv;
+ int ret;
+
+ mutex_lock(&ax_local->spi_lock);
+ AX_WRITE(&ax_local->ax_spi, val, P2_MDIODR);
+
+ AX_WRITE(&ax_local->ax_spi,
+ MDIOCR_RADDR(loc) | MDIOCR_FADDR(phy_id)
+ | MDIOCR_WRITE, P2_MDIOCR);
+
+ ret = read_poll_timeout(AX_READ, ret,
+ ((ret & MDIOCR_VALID) != 0), 0,
+ jiffies_to_usecs(HZ / 100), false,
+ &ax_local->ax_spi, P2_MDIOCR);
+ mutex_unlock(&ax_local->spi_lock);
+
+ return ret;
+}
+
+const struct ethtool_ops ax88796c_ethtool_ops = {
+ .get_drvinfo = ax88796c_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_msglevel = ax88796c_get_msglevel,
+ .set_msglevel = ax88796c_set_msglevel,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+ .nway_reset = phy_ethtool_nway_reset,
+ .get_pauseparam = ax88796c_get_pauseparam,
+ .set_pauseparam = ax88796c_set_pauseparam,
+ .get_regs_len = ax88796c_get_regs_len,
+ .get_regs = ax88796c_get_regs,
+ .get_tunable = ax88796c_get_tunable,
+ .set_tunable = ax88796c_set_tunable,};
+
+int ax88796c_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+ int ret;
+
+ ret = phy_mii_ioctl(ndev->phydev, ifr, cmd);
+
+ return ret;
+}
diff --git a/drivers/net/ethernet/asix/ax88796c_ioctl.h b/drivers/net/ethernet/asix/ax88796c_ioctl.h
new file mode 100644
index 000000000000..34d2a7dcc5ef
--- /dev/null
+++ b/drivers/net/ethernet/asix/ax88796c_ioctl.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2010 ASIX Electronics Corporation
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * ASIX AX88796C SPI Fast Ethernet Linux driver
+ */
+
+#ifndef _AX88796C_IOCTL_H
+#define _AX88796C_IOCTL_H
+
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+
+#include "ax88796c_main.h"
+
+extern const struct ethtool_ops ax88796c_ethtool_ops;
+
+bool ax88796c_check_power(const struct ax88796c_device *ax_local);
+bool ax88796c_check_power_and_wake(struct ax88796c_device *ax_local);
+void ax88796c_set_power_saving(struct ax88796c_device *ax_local, u8 ps_level);
+int ax88796c_mdio_read(struct mii_bus *mdiobus, int phy_id, int loc);
+int ax88796c_mdio_write(struct mii_bus *mdiobus, int phy_id, int loc, u16 val);
+int ax88796c_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+#endif
diff --git a/drivers/net/ethernet/asix/ax88796c_main.c b/drivers/net/ethernet/asix/ax88796c_main.c
new file mode 100644
index 000000000000..430164db56a1
--- /dev/null
+++ b/drivers/net/ethernet/asix/ax88796c_main.c
@@ -0,0 +1,1132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2010 ASIX Electronics Corporation
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * ASIX AX88796C SPI Fast Ethernet Linux driver
+ */
+
+#define pr_fmt(fmt) "ax88796c: " fmt
+
+#include "ax88796c_main.h"
+#include "ax88796c_ioctl.h"
+
+#include <linux/bitmap.h>
+#include <linux/etherdevice.h>
+#include <linux/iopoll.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/phy.h>
+#include <linux/spi/spi.h>
+
+static int comp = IS_ENABLED(CONFIG_SPI_AX88796C_COMPRESSION);
+static int msg_enable = NETIF_MSG_PROBE |
+ NETIF_MSG_LINK |
+ NETIF_MSG_RX_ERR |
+ NETIF_MSG_TX_ERR;
+
+static char *no_regs_list = "80018001,e1918001,8001a001,fc0d0000";
+unsigned long ax88796c_no_regs_mask[AX88796C_REGDUMP_LEN / (sizeof(unsigned long) * 8)];
+
+module_param(msg_enable, int, 0444);
+MODULE_PARM_DESC(msg_enable, "Message mask (see linux/netdevice.h for bitmap)");
+
+static int ax88796c_soft_reset(struct ax88796c_device *ax_local)
+{
+ u16 temp;
+ int ret;
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ AX_WRITE(&ax_local->ax_spi, PSR_RESET, P0_PSR);
+ AX_WRITE(&ax_local->ax_spi, PSR_RESET_CLR, P0_PSR);
+
+ ret = read_poll_timeout(AX_READ, ret,
+ (ret & PSR_DEV_READY),
+ 0, jiffies_to_usecs(160 * HZ / 1000), false,
+ &ax_local->ax_spi, P0_PSR);
+ if (ret)
+ return ret;
+
+ temp = AX_READ(&ax_local->ax_spi, P4_SPICR);
+ if (ax_local->capabilities & AX_CAP_COMP) {
+ AX_WRITE(&ax_local->ax_spi,
+ (temp | SPICR_RCEN | SPICR_QCEN), P4_SPICR);
+ ax_local->ax_spi.comp = 1;
+ } else {
+ AX_WRITE(&ax_local->ax_spi,
+ (temp & ~(SPICR_RCEN | SPICR_QCEN)), P4_SPICR);
+ ax_local->ax_spi.comp = 0;
+ }
+
+ return 0;
+}
+
+static int ax88796c_reload_eeprom(struct ax88796c_device *ax_local)
+{
+ int ret;
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ AX_WRITE(&ax_local->ax_spi, EECR_RELOAD, P3_EECR);
+
+ ret = read_poll_timeout(AX_READ, ret,
+ (ret & PSR_DEV_READY),
+ 0, jiffies_to_usecs(2 * HZ / 1000), false,
+ &ax_local->ax_spi, P0_PSR);
+ if (ret) {
+ dev_err(&ax_local->spi->dev,
+ "timeout waiting for reload eeprom\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ax88796c_set_hw_multicast(struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+ int mc_count = netdev_mc_count(ndev);
+ u16 rx_ctl = RXCR_AB;
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ memset(ax_local->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+
+ if (ndev->flags & IFF_PROMISC) {
+ rx_ctl |= RXCR_PRO;
+
+ } else if (ndev->flags & IFF_ALLMULTI || mc_count > AX_MAX_MCAST) {
+ rx_ctl |= RXCR_AMALL;
+
+ } else if (mc_count == 0) {
+ /* just broadcast and directed */
+ } else {
+ u32 crc_bits;
+ int i;
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ crc_bits = ether_crc(ETH_ALEN, ha->addr);
+ ax_local->multi_filter[crc_bits >> 29] |=
+ (1 << ((crc_bits >> 26) & 7));
+ }
+
+ for (i = 0; i < 4; i++) {
+ AX_WRITE(&ax_local->ax_spi,
+ ((ax_local->multi_filter[i * 2 + 1] << 8) |
+ ax_local->multi_filter[i * 2]), P3_MFAR(i));
+ }
+ }
+
+ AX_WRITE(&ax_local->ax_spi, rx_ctl, P2_RXCR);
+}
+
+static void ax88796c_set_mac_addr(struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ AX_WRITE(&ax_local->ax_spi, ((u16)(ndev->dev_addr[4] << 8) |
+ (u16)ndev->dev_addr[5]), P3_MACASR0);
+ AX_WRITE(&ax_local->ax_spi, ((u16)(ndev->dev_addr[2] << 8) |
+ (u16)ndev->dev_addr[3]), P3_MACASR1);
+ AX_WRITE(&ax_local->ax_spi, ((u16)(ndev->dev_addr[0] << 8) |
+ (u16)ndev->dev_addr[1]), P3_MACASR2);
+}
+
+static void ax88796c_load_mac_addr(struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+ u16 temp;
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ /* Try the device tree first */
+ if (!eth_platform_get_mac_address(&ax_local->spi->dev, ndev->dev_addr) &&
+ is_valid_ether_addr(ndev->dev_addr)) {
+ if (netif_msg_probe(ax_local))
+ dev_info(&ax_local->spi->dev,
+ "MAC address read from device tree\n");
+ return;
+ }
+
+ /* Read the MAC address from AX88796C */
+ temp = AX_READ(&ax_local->ax_spi, P3_MACASR0);
+ ndev->dev_addr[5] = (u8)temp;
+ ndev->dev_addr[4] = (u8)(temp >> 8);
+
+ temp = AX_READ(&ax_local->ax_spi, P3_MACASR1);
+ ndev->dev_addr[3] = (u8)temp;
+ ndev->dev_addr[2] = (u8)(temp >> 8);
+
+ temp = AX_READ(&ax_local->ax_spi, P3_MACASR2);
+ ndev->dev_addr[1] = (u8)temp;
+ ndev->dev_addr[0] = (u8)(temp >> 8);
+
+ if (is_valid_ether_addr(ndev->dev_addr)) {
+ if (netif_msg_probe(ax_local))
+ dev_info(&ax_local->spi->dev,
+ "MAC address read from ASIX chip\n");
+ return;
+ }
+
+ /* Use random address if none found */
+ if (netif_msg_probe(ax_local))
+ dev_info(&ax_local->spi->dev, "Use random MAC address\n");
+ eth_hw_addr_random(ndev);
+}
+
+static void ax88796c_proc_tx_hdr(struct tx_pkt_info *info, u8 ip_summed)
+{
+ u16 pkt_len_bar = (~info->pkt_len & TX_HDR_SOP_PKTLENBAR);
+
+ /* Prepare SOP header */
+ info->sop.flags_len = info->pkt_len |
+ ((ip_summed == CHECKSUM_NONE) ||
+ (ip_summed == CHECKSUM_UNNECESSARY) ? TX_HDR_SOP_DICF : 0);
+
+ info->sop.seq_lenbar = ((info->seq_num << 11) & TX_HDR_SOP_SEQNUM)
+ | pkt_len_bar;
+ cpu_to_be16s(&info->sop.flags_len);
+ cpu_to_be16s(&info->sop.seq_lenbar);
+
+ /* Prepare Segment header */
+ info->seg.flags_seqnum_seglen = TX_HDR_SEG_FS | TX_HDR_SEG_LS
+ | info->pkt_len;
+
+ info->seg.eo_so_seglenbar = pkt_len_bar;
+
+ cpu_to_be16s(&info->seg.flags_seqnum_seglen);
+ cpu_to_be16s(&info->seg.eo_so_seglenbar);
+
+ /* Prepare EOP header */
+ info->eop.seq_len = ((info->seq_num << 11) &
+ TX_HDR_EOP_SEQNUM) | info->pkt_len;
+ info->eop.seqbar_lenbar = ((~info->seq_num << 11) &
+ TX_HDR_EOP_SEQNUMBAR) | pkt_len_bar;
+
+ cpu_to_be16s(&info->eop.seq_len);
+ cpu_to_be16s(&info->eop.seqbar_lenbar);
+}
+
+static int
+ax88796c_check_free_pages(struct ax88796c_device *ax_local, u8 need_pages)
+{
+ u8 free_pages;
+ u16 tmp;
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ free_pages = AX_READ(&ax_local->ax_spi, P0_TFBFCR) & TX_FREEBUF_MASK;
+ if (free_pages < need_pages) {
+ /* schedule free page interrupt */
+ tmp = AX_READ(&ax_local->ax_spi, P0_TFBFCR)
+ & TFBFCR_SCHE_FREE_PAGE;
+ AX_WRITE(&ax_local->ax_spi, tmp | TFBFCR_TX_PAGE_SET |
+ TFBFCR_SET_FREE_PAGE(need_pages),
+ P0_TFBFCR);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static struct sk_buff *
+ax88796c_tx_fixup(struct net_device *ndev, struct sk_buff_head *q)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+ u8 spi_len = ax_local->ax_spi.comp ? 1 : 4;
+ struct sk_buff *skb, *tx_skb;
+ struct tx_pkt_info *info;
+ struct skb_data *entry;
+ u16 tol_len, pkt_len;
+ u8 padlen, seq_num;
+ u8 need_pages;
+ int headroom;
+ int tailroom;
+
+ if (skb_queue_empty(q))
+ return NULL;
+
+ skb = skb_peek(q);
+ pkt_len = skb->len;
+ need_pages = (pkt_len + TX_OVERHEAD + 127) >> 7;
+ if (ax88796c_check_free_pages(ax_local, need_pages) != 0)
+ return NULL;
+
+ headroom = skb_headroom(skb);
+ tailroom = skb_tailroom(skb);
+ padlen = ((pkt_len + 3) & 0x7FC) - pkt_len;
+ tol_len = ((pkt_len + 3) & 0x7FC) +
+ TX_OVERHEAD + TX_EOP_SIZE + spi_len;
+ seq_num = ++ax_local->seq_num & 0x1F;
+
+ info = (struct tx_pkt_info *)skb->cb;
+ info->pkt_len = pkt_len;
+
+ if ((!skb_cloned(skb)) &&
+ (headroom >= (TX_OVERHEAD + spi_len)) &&
+ (tailroom >= (padlen + TX_EOP_SIZE))) {
+ info->seq_num = seq_num;
+ ax88796c_proc_tx_hdr(info, skb->ip_summed);
+
+ /* SOP and SEG header */
+ memcpy(skb_push(skb, TX_OVERHEAD), &info->sop, TX_OVERHEAD);
+
+ /* Write SPI TXQ header */
+ memcpy(skb_push(skb, spi_len), ax88796c_tx_cmd_buf, spi_len);
+
+ /* Make 32-bit alignment */
+ skb_put(skb, padlen);
+
+ /* EOP header */
+ memcpy(skb_put(skb, TX_EOP_SIZE), &info->eop, TX_EOP_SIZE);
+
+ tx_skb = skb;
+ skb_unlink(skb, q);
+ } else {
+ tx_skb = alloc_skb(tol_len, GFP_KERNEL);
+ if (!tx_skb)
+ return NULL;
+
+ /* Write SPI TXQ header */
+ memcpy(skb_put(tx_skb, spi_len), ax88796c_tx_cmd_buf, spi_len);
+
+ info->seq_num = seq_num;
+ ax88796c_proc_tx_hdr(info, skb->ip_summed);
+
+ /* SOP and SEG header */
+ memcpy(skb_put(tx_skb, TX_OVERHEAD),
+ &info->sop, TX_OVERHEAD);
+
+ /* Packet */
+ memcpy(skb_put(tx_skb, ((pkt_len + 3) & 0xFFFC)),
+ skb->data, pkt_len);
+
+ /* EOP header */
+ memcpy(skb_put(tx_skb, TX_EOP_SIZE),
+ &info->eop, TX_EOP_SIZE);
+
+ skb_unlink(skb, q);
+ dev_kfree_skb(skb);
+ }
+
+ entry = (struct skb_data *)tx_skb->cb;
+ memset(entry, 0, sizeof(*entry));
+ entry->len = pkt_len;
+
+ if (netif_msg_pktdata(ax_local)) {
+ char pfx[IFNAMSIZ + 7];
+
+ snprintf(pfx, sizeof(pfx), "%s: ", ndev->name);
+
+ netdev_info(ndev, "TX packet len %d, total len %d, seq %d\n",
+ pkt_len, tx_skb->len, seq_num);
+
+ netdev_info(ndev, " SPI Header:\n");
+ print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
+ tx_skb->data, 4, 0);
+
+ netdev_info(ndev, " TX SOP:\n");
+ print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
+ tx_skb->data + 4, TX_OVERHEAD, 0);
+
+ netdev_info(ndev, " TX packet:\n");
+ print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
+ tx_skb->data + 4 + TX_OVERHEAD,
+ tx_skb->len - TX_EOP_SIZE - 4 - TX_OVERHEAD, 0);
+
+ netdev_info(ndev, " TX EOP:\n");
+ print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
+ tx_skb->data + tx_skb->len - 4, 4, 0);
+ }
+
+ return tx_skb;
+}
+
+static int ax88796c_hard_xmit(struct ax88796c_device *ax_local)
+{
+ struct sk_buff *tx_skb;
+ struct skb_data *entry;
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ tx_skb = ax88796c_tx_fixup(ax_local->ndev, &ax_local->tx_wait_q);
+
+ if (!tx_skb)
+ return 0;
+
+ entry = (struct skb_data *)tx_skb->cb;
+
+ AX_WRITE(&ax_local->ax_spi,
+ (TSNR_TXB_START | TSNR_PKT_CNT(1)), P0_TSNR);
+
+ axspi_write_txq(&ax_local->ax_spi, tx_skb->data, tx_skb->len);
+
+ if (((AX_READ(&ax_local->ax_spi, P0_TSNR) & TXNR_TXB_IDLE) == 0) ||
+ ((ISR_TXERR & AX_READ(&ax_local->ax_spi, P0_ISR)) != 0)) {
+ /* Ack tx error int */
+ AX_WRITE(&ax_local->ax_spi, ISR_TXERR, P0_ISR);
+
+ ax_local->stats.tx_dropped++;
+
+ netif_err(ax_local, tx_err, ax_local->ndev,
+ "TX FIFO error, re-initialize the TX bridge\n");
+
+ /* Reinitial tx bridge */
+ AX_WRITE(&ax_local->ax_spi, TXNR_TXB_REINIT |
+ AX_READ(&ax_local->ax_spi, P0_TSNR), P0_TSNR);
+ ax_local->seq_num = 0;
+ } else {
+ ax_local->stats.tx_packets++;
+ ax_local->stats.tx_bytes += entry->len;
+ }
+
+ entry->state = tx_done;
+ dev_kfree_skb(tx_skb);
+
+ return 1;
+}
+
+static int
+ax88796c_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+
+ skb_queue_tail(&ax_local->tx_wait_q, skb);
+ if (skb_queue_len(&ax_local->tx_wait_q) > TX_QUEUE_HIGH_WATER) {
+ netif_err(ax_local, tx_queued, ndev,
+ "Too many TX packets in queue %d\n",
+ skb_queue_len(&ax_local->tx_wait_q));
+
+ netif_stop_queue(ndev);
+ }
+
+ set_bit(EVENT_TX, &ax_local->flags);
+ schedule_work(&ax_local->ax_work);
+
+ return NETDEV_TX_OK;
+}
+
+static void
+ax88796c_skb_return(struct ax88796c_device *ax_local, struct sk_buff *skb,
+ struct rx_header *rxhdr)
+{
+ struct net_device *ndev = ax_local->ndev;
+ int status;
+
+ do {
+ if (!(ndev->features & NETIF_F_RXCSUM))
+ break;
+
+ /* checksum error bit is set */
+ if ((rxhdr->flags & RX_HDR3_L3_ERR) ||
+ (rxhdr->flags & RX_HDR3_L4_ERR))
+ break;
+
+ /* Other types may be indicated by more than one bit. */
+ if ((rxhdr->flags & RX_HDR3_L4_TYPE_TCP) ||
+ (rxhdr->flags & RX_HDR3_L4_TYPE_UDP))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } while (0);
+
+ ax_local->stats.rx_packets++;
+ ax_local->stats.rx_bytes += skb->len;
+ skb->dev = ndev;
+
+ skb->truesize = skb->len + sizeof(struct sk_buff);
+ skb->protocol = eth_type_trans(skb, ax_local->ndev);
+
+ netif_info(ax_local, rx_status, ndev, "< rx, len %zu, type 0x%x\n",
+ skb->len + sizeof(struct ethhdr), skb->protocol);
+
+ status = netif_rx(skb);
+ if (status != NET_RX_SUCCESS)
+ netif_info(ax_local, rx_err, ndev,
+ "netif_rx status %d\n", status);
+}
+
+static void
+ax88796c_rx_fixup(struct ax88796c_device *ax_local, struct sk_buff *rx_skb)
+{
+ struct rx_header *rxhdr = (struct rx_header *)rx_skb->data;
+ struct net_device *ndev = ax_local->ndev;
+ u16 len;
+
+ be16_to_cpus(&rxhdr->flags_len);
+ be16_to_cpus(&rxhdr->seq_lenbar);
+ be16_to_cpus(&rxhdr->flags);
+
+ if ((((short)rxhdr->flags_len) & RX_HDR1_PKT_LEN) !=
+ (~((short)rxhdr->seq_lenbar) & 0x7FF)) {
+ netif_err(ax_local, rx_err, ndev, "Header error\n");
+
+ ax_local->stats.rx_frame_errors++;
+ kfree_skb(rx_skb);
+ return;
+ }
+
+ if ((rxhdr->flags_len & RX_HDR1_MII_ERR) ||
+ (rxhdr->flags_len & RX_HDR1_CRC_ERR)) {
+ netif_err(ax_local, rx_err, ndev, "CRC or MII error\n");
+
+ ax_local->stats.rx_crc_errors++;
+ kfree_skb(rx_skb);
+ return;
+ }
+
+ len = rxhdr->flags_len & RX_HDR1_PKT_LEN;
+ if (netif_msg_pktdata(ax_local)) {
+ char pfx[IFNAMSIZ + 7];
+
+ snprintf(pfx, sizeof(pfx), "%s: ", ndev->name);
+ netdev_info(ndev, "RX data, total len %d, packet len %d\n",
+ rx_skb->len, len);
+
+ netdev_info(ndev, " Dump RX packet header:");
+ print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
+ rx_skb->data, sizeof(*rxhdr), 0);
+
+ netdev_info(ndev, " Dump RX packet:");
+ print_hex_dump(KERN_INFO, pfx, DUMP_PREFIX_OFFSET, 16, 1,
+ rx_skb->data + sizeof(*rxhdr), len, 0);
+ }
+
+ skb_pull(rx_skb, sizeof(*rxhdr));
+ __pskb_trim(rx_skb, len);
+
+ return ax88796c_skb_return(ax_local, rx_skb, rxhdr);
+}
+
+static int ax88796c_receive(struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+ struct skb_data *entry;
+ u16 w_count, pkt_len;
+ struct sk_buff *skb;
+ u8 pkt_cnt;
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ /* check rx packet and total word count */
+ AX_WRITE(&ax_local->ax_spi, AX_READ(&ax_local->ax_spi, P0_RTWCR)
+ | RTWCR_RX_LATCH, P0_RTWCR);
+
+ pkt_cnt = AX_READ(&ax_local->ax_spi, P0_RXBCR2) & RXBCR2_PKT_MASK;
+ if (!pkt_cnt)
+ return 0;
+
+ pkt_len = AX_READ(&ax_local->ax_spi, P0_RCPHR) & 0x7FF;
+
+ w_count = ((pkt_len + 6 + 3) & 0xFFFC) >> 1;
+
+ skb = alloc_skb((w_count * 2), GFP_ATOMIC);
+ if (!skb) {
+ AX_WRITE(&ax_local->ax_spi, RXBCR1_RXB_DISCARD, P0_RXBCR1);
+ return 0;
+ }
+ entry = (struct skb_data *)skb->cb;
+
+ AX_WRITE(&ax_local->ax_spi, RXBCR1_RXB_START | w_count, P0_RXBCR1);
+
+ axspi_read_rxq(&ax_local->ax_spi,
+ skb_put(skb, w_count * 2), skb->len);
+
+ /* Check if rx bridge is idle */
+ if ((AX_READ(&ax_local->ax_spi, P0_RXBCR2) & RXBCR2_RXB_IDLE) == 0) {
+ netif_err(ax_local, rx_err, ndev,
+ "Rx Bridge is not idle\n");
+ AX_WRITE(&ax_local->ax_spi, RXBCR2_RXB_REINIT, P0_RXBCR2);
+
+ entry->state = rx_err;
+ } else {
+ entry->state = rx_done;
+ }
+
+ AX_WRITE(&ax_local->ax_spi, ISR_RXPKT, P0_ISR);
+
+ ax88796c_rx_fixup(ax_local, skb);
+
+ return 1;
+}
+
+static int ax88796c_process_isr(struct ax88796c_device *ax_local)
+{
+ struct net_device *ndev = ax_local->ndev;
+ u8 done = 0;
+ u16 isr;
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ isr = AX_READ(&ax_local->ax_spi, P0_ISR);
+ AX_WRITE(&ax_local->ax_spi, isr, P0_ISR);
+
+ netif_dbg(ax_local, intr, ndev, " ISR 0x%04x\n", isr);
+
+ if (isr & ISR_TXERR) {
+ netif_dbg(ax_local, intr, ndev, " TXERR interrupt\n");
+ AX_WRITE(&ax_local->ax_spi, TXNR_TXB_REINIT, P0_TSNR);
+ ax_local->seq_num = 0x1f;
+ }
+
+ if (isr & ISR_TXPAGES) {
+ netif_dbg(ax_local, intr, ndev, " TXPAGES interrupt\n");
+ set_bit(EVENT_TX, &ax_local->flags);
+ }
+
+ if (isr & ISR_LINK) {
+ netif_dbg(ax_local, intr, ndev, " Link change interrupt\n");
+ phy_mac_interrupt(ax_local->ndev->phydev);
+ }
+
+ if (isr & ISR_RXPKT) {
+ netif_dbg(ax_local, intr, ndev, " RX interrupt\n");
+ done = ax88796c_receive(ax_local->ndev);
+ }
+
+ return done;
+}
+
+static irqreturn_t ax88796c_interrupt(int irq, void *dev_instance)
+{
+ struct ax88796c_device *ax_local;
+ struct net_device *ndev;
+
+ ndev = dev_instance;
+ if (!ndev) {
+ pr_err("irq %d for unknown device.\n", irq);
+ return IRQ_RETVAL(0);
+ }
+ ax_local = to_ax88796c_device(ndev);
+
+ disable_irq_nosync(irq);
+
+ netif_dbg(ax_local, intr, ndev, "Interrupt occurred\n");
+
+ set_bit(EVENT_INTR, &ax_local->flags);
+ schedule_work(&ax_local->ax_work);
+
+ return IRQ_HANDLED;
+}
+
+static void ax88796c_work(struct work_struct *work)
+{
+ struct ax88796c_device *ax_local =
+ container_of(work, struct ax88796c_device, ax_work);
+
+ mutex_lock(&ax_local->spi_lock);
+
+ if (test_bit(EVENT_SET_MULTI, &ax_local->flags)) {
+ ax88796c_set_hw_multicast(ax_local->ndev);
+ clear_bit(EVENT_SET_MULTI, &ax_local->flags);
+ }
+
+ if (test_bit(EVENT_INTR, &ax_local->flags)) {
+ AX_WRITE(&ax_local->ax_spi, IMR_MASKALL, P0_IMR);
+
+ while (1) {
+ if (!ax88796c_process_isr(ax_local))
+ break;
+ }
+
+ clear_bit(EVENT_INTR, &ax_local->flags);
+
+ AX_WRITE(&ax_local->ax_spi, IMR_DEFAULT, P0_IMR);
+
+ enable_irq(ax_local->ndev->irq);
+ }
+
+ if (test_bit(EVENT_TX, &ax_local->flags)) {
+ while (skb_queue_len(&ax_local->tx_wait_q)) {
+ if (!ax88796c_hard_xmit(ax_local))
+ break;
+ }
+
+ clear_bit(EVENT_TX, &ax_local->flags);
+
+ if (netif_queue_stopped(ax_local->ndev) &&
+ (skb_queue_len(&ax_local->tx_wait_q) < TX_QUEUE_LOW_WATER))
+ netif_wake_queue(ax_local->ndev);
+ }
+
+ mutex_unlock(&ax_local->spi_lock);
+}
+
+static struct net_device_stats *ax88796c_get_stats(struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+
+ return &ax_local->stats;
+}
+
+static void ax88796c_set_mac(struct ax88796c_device *ax_local)
+{
+ u16 maccr;
+
+ maccr = (ax_local->link) ? MACCR_RXEN : 0;
+
+ switch (ax_local->speed) {
+ case SPEED_100:
+ maccr |= MACCR_SPEED_100;
+ case SPEED_10:
+ case SPEED_UNKNOWN:
+ break;
+ default:
+ return;
+ }
+
+ switch (ax_local->duplex) {
+ case DUPLEX_FULL:
+ maccr |= MACCR_SPEED_100;
+ case DUPLEX_HALF:
+ case DUPLEX_UNKNOWN:
+ break;
+ default:
+ return;
+ }
+
+ if (ax_local->flowctrl & AX_FC_ANEG &&
+ ax_local->phydev->autoneg) {
+ maccr |= ax_local->pause ? MACCR_RXFC_ENABLE : 0;
+ maccr |= !ax_local->pause != !ax_local->asym_pause ?
+ MACCR_TXFC_ENABLE : 0;
+ } else {
+ maccr |= (ax_local->flowctrl & AX_FC_RX) ? MACCR_RXFC_ENABLE : 0;
+ maccr |= (ax_local->flowctrl & AX_FC_TX) ? MACCR_TXFC_ENABLE : 0;
+ }
+
+ mutex_lock(&ax_local->spi_lock);
+
+ maccr |= AX_READ(&ax_local->ax_spi, P0_MACCR) &
+ ~(MACCR_DUPLEX_FULL | MACCR_SPEED_100 |
+ MACCR_TXFC_ENABLE | MACCR_RXFC_ENABLE);
+ AX_WRITE(&ax_local->ax_spi, maccr, P0_MACCR);
+
+ mutex_unlock(&ax_local->spi_lock);
+}
+
+static void ax88796c_handle_link_change(struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+ struct phy_device *phydev = ndev->phydev;
+ bool update = false;
+
+ if (phydev->link && (ax_local->speed != phydev->speed ||
+ ax_local->duplex != phydev->duplex ||
+ ax_local->pause != phydev->pause ||
+ ax_local->asym_pause != phydev->asym_pause)) {
+ ax_local->speed = phydev->speed;
+ ax_local->duplex = phydev->duplex;
+ ax_local->pause = phydev->pause;
+ ax_local->asym_pause = phydev->asym_pause;
+ update = true;
+ }
+
+ if (phydev->link != ax_local->link) {
+ if (!phydev->link) {
+ ax_local->speed = SPEED_UNKNOWN;
+ ax_local->duplex = DUPLEX_UNKNOWN;
+ }
+
+ ax_local->link = phydev->link;
+ update = true;
+ }
+
+ if (update)
+ ax88796c_set_mac(ax_local);
+
+ if (net_ratelimit())
+ phy_print_status(ndev->phydev);
+}
+
+static void ax88796c_set_csums(struct ax88796c_device *ax_local)
+{
+ struct net_device *ndev = ax_local->ndev;
+
+ WARN_ON(!mutex_is_locked(&ax_local->spi_lock));
+
+ if (ndev->features & NETIF_F_RXCSUM) {
+ AX_WRITE(&ax_local->ax_spi, COERCR0_DEFAULT, P4_COERCR0);
+ AX_WRITE(&ax_local->ax_spi, COERCR1_DEFAULT, P4_COERCR1);
+ } else {
+ AX_WRITE(&ax_local->ax_spi, 0, P4_COERCR0);
+ AX_WRITE(&ax_local->ax_spi, 0, P4_COERCR1);
+ }
+
+ if (ndev->features & NETIF_F_HW_CSUM) {
+ AX_WRITE(&ax_local->ax_spi, COETCR0_DEFAULT, P4_COETCR0);
+ AX_WRITE(&ax_local->ax_spi, COETCR1_TXPPPE, P4_COETCR1);
+ } else {
+ AX_WRITE(&ax_local->ax_spi, 0, P4_COETCR0);
+ AX_WRITE(&ax_local->ax_spi, 0, P4_COETCR1);
+ }
+}
+
+static int
+ax88796c_open(struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+ unsigned long irq_flag = IRQF_SHARED;
+ int fc = AX_FC_NONE;
+ int ret;
+
+ ret = request_irq(ndev->irq, ax88796c_interrupt,
+ irq_flag, ndev->name, ndev);
+ if (ret) {
+ netdev_err(ndev, "unable to get IRQ %d (errno=%d).\n",
+ ndev->irq, ret);
+ return ret;
+ }
+
+ mutex_lock(&ax_local->spi_lock);
+
+ ret = ax88796c_soft_reset(ax_local);
+ if (ret < 0) {
+ mutex_unlock(&ax_local->spi_lock);
+ return ret;
+ }
+ ax_local->seq_num = 0x1f;
+
+ ax88796c_set_mac_addr(ndev);
+ ax88796c_set_csums(ax_local);
+
+ /* Disable stuffing packet */
+ AX_WRITE(&ax_local->ax_spi,
+ AX_READ(&ax_local->ax_spi, P1_RXBSPCR)
+ & ~RXBSPCR_STUF_ENABLE, P1_RXBSPCR);
+
+ /* Enable RX packet process */
+ AX_WRITE(&ax_local->ax_spi, RPPER_RXEN, P1_RPPER);
+
+ AX_WRITE(&ax_local->ax_spi, AX_READ(&ax_local->ax_spi, P0_FER)
+ | FER_RXEN | FER_TXEN | FER_BSWAP | FER_IRQ_PULL, P0_FER);
+
+ /* Setup LED mode */
+ AX_WRITE(&ax_local->ax_spi,
+ (LCR_LED0_EN | LCR_LED0_DUPLEX | LCR_LED1_EN |
+ LCR_LED1_100MODE), P2_LCR0);
+ AX_WRITE(&ax_local->ax_spi,
+ (AX_READ(&ax_local->ax_spi, P2_LCR1) & LCR_LED2_MASK) |
+ LCR_LED2_EN | LCR_LED2_LINK, P2_LCR1);
+
+ /* Disable PHY auto-polling */
+ AX_WRITE(&ax_local->ax_spi, PCR_PHYID(AX88796C_PHY_ID), P2_PCR);
+
+ /* Enable MAC interrupts */
+ AX_WRITE(&ax_local->ax_spi, IMR_DEFAULT, P0_IMR);
+
+ mutex_unlock(&ax_local->spi_lock);
+
+ /* Setup flow-control configuration */
+ phy_support_asym_pause(ax_local->phydev);
+
+ if (ax_local->phydev->advertising &&
+ (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+ ax_local->phydev->advertising) ||
+ linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ax_local->phydev->advertising)))
+ fc |= AX_FC_ANEG;
+
+ fc |= linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+ ax_local->phydev->advertising) ? AX_FC_RX : 0;
+ fc |= (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+ ax_local->phydev->advertising) !=
+ linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ax_local->phydev->advertising)) ? AX_FC_TX : 0;
+ ax_local->flowctrl = fc;
+
+ phy_start(ax_local->ndev->phydev);
+
+ netif_start_queue(ndev);
+
+ spi_message_init(&ax_local->ax_spi.rx_msg);
+
+ return 0;
+}
+
+static void ax88796c_free_skb_queue(struct sk_buff_head *q)
+{
+ struct sk_buff *skb;
+
+ while (q->qlen) {
+ skb = skb_dequeue(q);
+ kfree_skb(skb);
+ }
+}
+
+static int
+ax88796c_close(struct net_device *ndev)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+
+ netif_stop_queue(ndev);
+ phy_stop(ndev->phydev);
+
+ mutex_lock(&ax_local->spi_lock);
+
+ /* Disable MAC interrupts */
+ AX_WRITE(&ax_local->ax_spi, IMR_MASKALL, P0_IMR);
+ ax88796c_free_skb_queue(&ax_local->tx_wait_q);
+ ax88796c_soft_reset(ax_local);
+
+ mutex_unlock(&ax_local->spi_lock);
+
+ free_irq(ndev->irq, ndev);
+
+ return 0;
+}
+
+static int
+ax88796c_set_features(struct net_device *ndev, netdev_features_t features)
+{
+ struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
+ netdev_features_t changed = features ^ ndev->features;
+
+ if (!(changed & (NETIF_F_RXCSUM | NETIF_F_HW_CSUM)))
+ return 0;
+
+ ndev->features = features;
+
+ if (changed & (NETIF_F_RXCSUM | NETIF_F_HW_CSUM))
+ ax88796c_set_csums(ax_local);
+
+ return 0;
+}
+
+static const struct net_device_ops ax88796c_netdev_ops = {
+ .ndo_open = ax88796c_open,
+ .ndo_stop = ax88796c_close,
+ .ndo_start_xmit = ax88796c_start_xmit,
+ .ndo_get_stats = ax88796c_get_stats,
+ .ndo_do_ioctl = ax88796c_ioctl,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_features = ax88796c_set_features,
+};
+
+static int ax88796c_hard_reset(struct ax88796c_device *ax_local)
+{
+ struct device *dev = (struct device *)&ax_local->spi->dev;
+ struct gpio_desc *reset_gpio;
+
+ /* reset info */
+ reset_gpio = gpiod_get(dev, "reset", 0);
+ if (IS_ERR(reset_gpio)) {
+ dev_err(dev, "Could not get 'reset' GPIO: %ld", PTR_ERR(reset_gpio));
+ return PTR_ERR(reset_gpio);
+ }
+
+ /* set reset */
+ gpiod_direction_output(reset_gpio, 1);
+ msleep(100);
+ gpiod_direction_output(reset_gpio, 0);
+ gpiod_put(reset_gpio);
+ msleep(20);
+
+ return 0;
+}
+
+static int ax88796c_probe(struct spi_device *spi)
+{
+ char phy_id[MII_BUS_ID_SIZE + 3];
+ struct ax88796c_device *ax_local;
+ struct net_device *ndev;
+ u16 temp;
+ int ret;
+
+ ndev = devm_alloc_etherdev(&spi->dev, sizeof(*ax_local));
+ if (!ndev)
+ return -ENOMEM;
+
+ SET_NETDEV_DEV(ndev, &spi->dev);
+
+ ax_local = to_ax88796c_device(ndev);
+ memset(ax_local, 0, sizeof(*ax_local));
+
+ dev_set_drvdata(&spi->dev, ax_local);
+ ax_local->spi = spi;
+ ax_local->ax_spi.spi = spi;
+
+ ax_local->ndev = ndev;
+ ax_local->capabilities |= comp ? AX_CAP_COMP : 0;
+ ax_local->msg_enable = msg_enable;
+ mutex_init(&ax_local->spi_lock);
+
+ ax_local->mdiobus = devm_mdiobus_alloc(&spi->dev);
+ if (!ax_local->mdiobus)
+ return -ENOMEM;
+
+ ax_local->mdiobus->priv = ax_local;
+ ax_local->mdiobus->read = ax88796c_mdio_read;
+ ax_local->mdiobus->write = ax88796c_mdio_write;
+ ax_local->mdiobus->name = "ax88976c-mdiobus";
+ ax_local->mdiobus->phy_mask = ~BIT(AX88796C_PHY_ID);
+ ax_local->mdiobus->parent = &spi->dev;
+
+ snprintf(ax_local->mdiobus->id, MII_BUS_ID_SIZE,
+ "ax88796c-%s.%u", dev_name(&spi->dev), spi->chip_select);
+
+ ret = devm_mdiobus_register(&spi->dev, ax_local->mdiobus);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Could not register MDIO bus\n");
+ return ret;
+ }
+
+ if (netif_msg_probe(ax_local)) {
+ dev_info(&spi->dev, "AX88796C-SPI Configuration:\n");
+ dev_info(&spi->dev, " Compression : %s\n",
+ ax_local->capabilities & AX_CAP_COMP ? "ON" : "OFF");
+ }
+
+ ndev->irq = spi->irq;
+ ndev->netdev_ops = &ax88796c_netdev_ops;
+ ndev->ethtool_ops = &ax88796c_ethtool_ops;
+ ndev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+ ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+ ndev->hard_header_len += (TX_OVERHEAD + 4);
+
+ mutex_lock(&ax_local->spi_lock);
+
+ /* ax88796c gpio reset */
+ ax88796c_hard_reset(ax_local);
+
+ /* Reset AX88796C */
+ ret = ax88796c_soft_reset(ax_local);
+ if (ret < 0) {
+ ret = -ENODEV;
+ goto err;
+ }
+ /* Check board revision */
+ temp = AX_READ(&ax_local->ax_spi, P2_CRIR);
+ if ((temp & 0xF) != 0x0) {
+ dev_err(&spi->dev, "spi read failed: %d\n", temp);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ temp = AX_READ(&ax_local->ax_spi, P0_BOR);
+ if (temp == 0x1234) {
+ ax_local->plat_endian = PLAT_LITTLE_ENDIAN;
+ } else {
+ AX_WRITE(&ax_local->ax_spi, 0xFFFF, P0_BOR);
+ ax_local->plat_endian = PLAT_BIG_ENDIAN;
+ }
+
+ /*Reload EEPROM*/
+ ax88796c_reload_eeprom(ax_local);
+
+ ax88796c_load_mac_addr(ndev);
+
+ if (netif_msg_probe(ax_local))
+ dev_info(&spi->dev,
+ "irq %d, MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n",
+ ndev->irq,
+ ndev->dev_addr[0], ndev->dev_addr[1],
+ ndev->dev_addr[2], ndev->dev_addr[3],
+ ndev->dev_addr[4], ndev->dev_addr[5]);
+
+ /* Disable power saving */
+ AX_WRITE(&ax_local->ax_spi, (AX_READ(&ax_local->ax_spi, P0_PSCR)
+ & PSCR_PS_MASK) | PSCR_PS_D0, P0_PSCR);
+
+ mutex_unlock(&ax_local->spi_lock);
+
+ INIT_WORK(&ax_local->ax_work, ax88796c_work);
+
+ skb_queue_head_init(&ax_local->tx_wait_q);
+
+ snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT,
+ ax_local->mdiobus->id, AX88796C_PHY_ID);
+ ax_local->phydev = phy_connect(ax_local->ndev, phy_id,
+ ax88796c_handle_link_change,
+ PHY_INTERFACE_MODE_MII);
+ if (IS_ERR(ax_local->phydev)) {
+ ret = PTR_ERR(ax_local->phydev);
+ goto err;
+ }
+ ax_local->phydev->irq = PHY_POLL;
+
+ ret = devm_register_netdev(&spi->dev, ndev);
+ if (ret) {
+ dev_err(&spi->dev, "failed to register a network device\n");
+ goto err_phy_dis;
+ }
+
+ netif_info(ax_local, probe, ndev, "%s %s registered\n",
+ dev_driver_string(&spi->dev),
+ dev_name(&spi->dev));
+ phy_attached_info(ax_local->phydev);
+
+ return 0;
+
+err_phy_dis:
+ phy_disconnect(ax_local->phydev);
+err:
+ return ret;
+}
+
+static int ax88796c_remove(struct spi_device *spi)
+{
+ struct ax88796c_device *ax_local = dev_get_drvdata(&spi->dev);
+ struct net_device *ndev = ax_local->ndev;
+
+ phy_disconnect(ndev->phydev);
+
+ netif_info(ax_local, probe, ndev, "removing network device %s %s\n",
+ dev_driver_string(&spi->dev),
+ dev_name(&spi->dev));
+
+ return 0;
+}
+
+static const struct of_device_id ax88796c_dt_ids[] = {
+ { .compatible = "asix,ax88796c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ax88796c_dt_ids);
+
+static const struct spi_device_id asix_id[] = {
+ { "ax88796c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, asix_id);
+
+static struct spi_driver ax88796c_spi_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(ax88796c_dt_ids),
+ },
+ .probe = ax88796c_probe,
+ .remove = ax88796c_remove,
+ .id_table = asix_id,
+};
+
+static __init int ax88796c_spi_init(void)
+{
+ int ret;
+
+ bitmap_zero(ax88796c_no_regs_mask, AX88796C_REGDUMP_LEN);
+ ret = bitmap_parse(no_regs_list, 35,
+ ax88796c_no_regs_mask, AX88796C_REGDUMP_LEN);
+ if (ret) {
+ bitmap_fill(ax88796c_no_regs_mask, AX88796C_REGDUMP_LEN);
+ pr_err("Invalid bitmap description, masking all registers\n");
+ }
+
+ return spi_register_driver(&ax88796c_spi_driver);
+}
+
+static __exit void ax88796c_spi_exit(void)
+{
+ spi_unregister_driver(&ax88796c_spi_driver);
+}
+
+module_init(ax88796c_spi_init);
+module_exit(ax88796c_spi_exit);
+
+MODULE_AUTHOR("ASIX");
+MODULE_DESCRIPTION("ASIX AX88796C SPI Ethernet driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/asix/ax88796c_main.h b/drivers/net/ethernet/asix/ax88796c_main.h
new file mode 100644
index 000000000000..229906d5737f
--- /dev/null
+++ b/drivers/net/ethernet/asix/ax88796c_main.h
@@ -0,0 +1,561 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2010 ASIX Electronics Corporation
+ * Copyright (c) 2020 Samsung Electronics
+ *
+ * ASIX AX88796C SPI Fast Ethernet Linux driver
+ */
+
+#ifndef _AX88796C_MAIN_H
+#define _AX88796C_MAIN_H
+
+#include <linux/netdevice.h>
+#include <linux/mii.h>
+
+#include "ax88796c_spi.h"
+
+/* These identify the driver base version and may not be removed. */
+#define DRV_NAME "ax88796c"
+#define ADP_NAME "ASIX AX88796C SPI Ethernet Adapter"
+
+#define TX_QUEUE_HIGH_WATER 45 /* Tx queue high water mark */
+#define TX_QUEUE_LOW_WATER 20 /* Tx queue low water mark */
+
+#define AX88796C_REGDUMP_LEN 256
+#define AX88796C_PHY_REGDUMP_LEN 14
+#define AX88796C_PHY_ID 0x10
+
+#define TX_OVERHEAD 8
+#define TX_EOP_SIZE 4
+
+#define AX_MCAST_FILTER_SIZE 8
+#define AX_MAX_MCAST 64
+#define AX_MAX_CLK 80000000
+#define TX_HDR_SOP_DICF 0x8000
+#define TX_HDR_SOP_CPHI 0x4000
+#define TX_HDR_SOP_INT 0x2000
+#define TX_HDR_SOP_MDEQ 0x1000
+#define TX_HDR_SOP_PKTLEN 0x07FF
+#define TX_HDR_SOP_SEQNUM 0xF800
+#define TX_HDR_SOP_PKTLENBAR 0x07FF
+
+#define TX_HDR_SEG_FS 0x8000
+#define TX_HDR_SEG_LS 0x4000
+#define TX_HDR_SEG_SEGNUM 0x3800
+#define TX_HDR_SEG_SEGLEN 0x0700
+#define TX_HDR_SEG_EOFST 0xC000
+#define TX_HDR_SEG_SOFST 0x3800
+#define TX_HDR_SEG_SEGLENBAR 0x07FF
+
+#define TX_HDR_EOP_SEQNUM 0xF800
+#define TX_HDR_EOP_PKTLEN 0x07FF
+#define TX_HDR_EOP_SEQNUMBAR 0xF800
+#define TX_HDR_EOP_PKTLENBAR 0x07FF
+
+/* Rx header fields mask */
+#define RX_HDR1_MCBC 0x8000
+#define RX_HDR1_STUFF_PKT 0x4000
+#define RX_HDR1_MII_ERR 0x2000
+#define RX_HDR1_CRC_ERR 0x1000
+#define RX_HDR1_PKT_LEN 0x07FF
+
+#define RX_HDR2_SEQ_NUM 0xF800
+#define RX_HDR2_PKT_LEN_BAR 0x7FFF
+
+#define RX_HDR3_PE 0x8000
+#define RX_HDR3_L3_TYPE_IPV4V6 0x6000
+#define RX_HDR3_L3_TYPE_IP 0x4000
+#define RX_HDR3_L3_TYPE_IPV6 0x2000
+#define RX_HDR3_L4_TYPE_ICMPV6 0x1400
+#define RX_HDR3_L4_TYPE_TCP 0x1000
+#define RX_HDR3_L4_TYPE_IGMP 0x0c00
+#define RX_HDR3_L4_TYPE_ICMP 0x0800
+#define RX_HDR3_L4_TYPE_UDP 0x0400
+#define RX_HDR3_L3_ERR 0x0200
+#define RX_HDR3_L4_ERR 0x0100
+#define RX_HDR3_PRIORITY(x) ((x) << 4)
+#define RX_HDR3_STRIP 0x0008
+#define RX_HDR3_VLAN_ID 0x0007
+
+struct ax88796c_device {
+ struct spi_device *spi;
+ struct net_device *ndev;
+ struct net_device_stats stats;
+
+ struct work_struct ax_work;
+
+ struct mutex spi_lock; /* device access */
+
+ struct sk_buff_head tx_wait_q;
+
+ struct axspi_data ax_spi;
+
+ struct mii_bus *mdiobus;
+ struct phy_device *phydev;
+
+ int msg_enable;
+
+ u16 seq_num;
+
+ u8 multi_filter[AX_MCAST_FILTER_SIZE];
+
+ int link;
+ int speed;
+ int duplex;
+ int pause;
+ int asym_pause;
+ int flowctrl;
+ #define AX_FC_NONE 0
+ #define AX_FC_RX BIT(0)
+ #define AX_FC_TX BIT(1)
+ #define AX_FC_ANEG BIT(2)
+
+ unsigned long capabilities;
+ #define AX_CAP_COMP BIT(0)
+
+ u8 plat_endian;
+ #define PLAT_LITTLE_ENDIAN 0
+ #define PLAT_BIG_ENDIAN 1
+
+ unsigned long flags;
+ #define EVENT_INTR BIT(0)
+ #define EVENT_TX BIT(1)
+ #define EVENT_SET_MULTI BIT(2)
+
+};
+
+#define to_ax88796c_device(ndev) ((struct ax88796c_device *)netdev_priv(ndev))
+
+enum skb_state {
+ illegal = 0,
+ tx_done,
+ rx_done,
+ rx_err,
+};
+
+struct skb_data {
+ enum skb_state state;
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ size_t len;
+};
+
+/* A88796C register definition */
+ /* Definition of PAGE0 */
+#define P0_PSR (0x00)
+ #define PSR_DEV_READY BIT(7)
+ #define PSR_RESET (0 << 15)
+ #define PSR_RESET_CLR BIT(15)
+#define P0_BOR (0x02)
+#define P0_FER (0x04)
+ #define FER_IPALM BIT(0)
+ #define FER_DCRC BIT(1)
+ #define FER_RH3M BIT(2)
+ #define FER_HEADERSWAP BIT(7)
+ #define FER_WSWAP BIT(8)
+ #define FER_BSWAP BIT(9)
+ #define FER_INTHI BIT(10)
+ #define FER_INTLO (0 << 10)
+ #define FER_IRQ_PULL BIT(11)
+ #define FER_RXEN BIT(14)
+ #define FER_TXEN BIT(15)
+#define P0_ISR (0x06)
+ #define ISR_RXPKT BIT(0)
+ #define ISR_MDQ BIT(4)
+ #define ISR_TXT BIT(5)
+ #define ISR_TXPAGES BIT(6)
+ #define ISR_TXERR BIT(8)
+ #define ISR_LINK BIT(9)
+#define P0_IMR (0x08)
+ #define IMR_RXPKT BIT(0)
+ #define IMR_MDQ BIT(4)
+ #define IMR_TXT BIT(5)
+ #define IMR_TXPAGES BIT(6)
+ #define IMR_TXERR BIT(8)
+ #define IMR_LINK BIT(9)
+ #define IMR_MASKALL (0xFFFF)
+ #define IMR_DEFAULT (IMR_TXERR)
+#define P0_WFCR (0x0A)
+ #define WFCR_PMEIND BIT(0) /* PME indication */
+ #define WFCR_PMETYPE BIT(1) /* PME I/O type */
+ #define WFCR_PMEPOL BIT(2) /* PME polarity */
+ #define WFCR_PMERST BIT(3) /* Reset PME */
+ #define WFCR_SLEEP BIT(4) /* Enable sleep mode */
+ #define WFCR_WAKEUP BIT(5) /* Enable wakeup mode */
+ #define WFCR_WAITEVENT BIT(6) /* Reserved */
+ #define WFCR_CLRWAKE BIT(7) /* Clear wakeup */
+ #define WFCR_LINKCH BIT(8) /* Enable link change */
+ #define WFCR_MAGICP BIT(9) /* Enable magic packet */
+ #define WFCR_WAKEF BIT(10) /* Enable wakeup frame */
+ #define WFCR_PMEEN BIT(11) /* Enable PME pin */
+ #define WFCR_LINKCHS BIT(12) /* Link change status */
+ #define WFCR_MAGICPS BIT(13) /* Magic packet status */
+ #define WFCR_WAKEFS BIT(14) /* Wakeup frame status */
+ #define WFCR_PMES BIT(15) /* PME pin status */
+#define P0_PSCR (0x0C)
+ #define PSCR_PS_MASK (0xFFF0)
+ #define PSCR_PS_D0 (0)
+ #define PSCR_PS_D1 BIT(0)
+ #define PSCR_PS_D2 BIT(1)
+ #define PSCR_FPS BIT(3) /* Enable fiber mode PS */
+ #define PSCR_SWPS BIT(4) /* Enable software */
+ /* PS control */
+ #define PSCR_WOLPS BIT(5) /* Enable WOL PS */
+ #define PSCR_SWWOL BIT(6) /* Enable software select */
+ /* WOL PS */
+ #define PSCR_PHYOSC BIT(7) /* Internal PHY OSC control */
+ #define PSCR_FOFEF BIT(8) /* Force PHY generate FEF */
+ #define PSCR_FOF BIT(9) /* Force PHY in fiber mode */
+ #define PSCR_PHYPD BIT(10) /* PHY power down. */
+ /* Active high */
+ #define PSCR_PHYRST BIT(11) /* PHY reset signal. */
+ /* Active low */
+ #define PSCR_PHYCSIL BIT(12) /* PHY cable energy detect */
+ #define PSCR_PHYCOFF BIT(13) /* PHY cable off */
+ #define PSCR_PHYLINK BIT(14) /* PHY link status */
+ #define PSCR_EEPOK BIT(15) /* EEPROM load complete */
+#define P0_MACCR (0x0E)
+ #define MACCR_RXEN BIT(0) /* Enable RX */
+ #define MACCR_DUPLEX_FULL BIT(1) /* 1: Full, 0: Half */
+ #define MACCR_SPEED_100 BIT(2) /* 1: 100Mbps, 0: 10Mbps */
+ #define MACCR_RXFC_ENABLE BIT(3)
+ #define MACCR_RXFC_MASK 0xFFF7
+ #define MACCR_TXFC_ENABLE BIT(4)
+ #define MACCR_TXFC_MASK 0xFFEF
+ #define MACCR_PSI BIT(6) /* Software Cable-Off */
+ /* Power Saving Interrupt */
+ #define MACCR_PF BIT(7)
+ #define MACCR_PMM_BITS 8
+ #define MACCR_PMM_MASK (0x1F00)
+ #define MACCR_PMM_RESET BIT(8)
+ #define MACCR_PMM_WAIT (2 << 8)
+ #define MACCR_PMM_READY (3 << 8)
+ #define MACCR_PMM_D1 (4 << 8)
+ #define MACCR_PMM_D2 (5 << 8)
+ #define MACCR_PMM_WAKE (7 << 8)
+ #define MACCR_PMM_D1_WAKE (8 << 8)
+ #define MACCR_PMM_D2_WAKE (9 << 8)
+ #define MACCR_PMM_SLEEP (10 << 8)
+ #define MACCR_PMM_PHY_RESET (11 << 8)
+ #define MACCR_PMM_SOFT_D1 (16 << 8)
+ #define MACCR_PMM_SOFT_D2 (17 << 8)
+#define P0_TFBFCR (0x10)
+ #define TFBFCR_SCHE_FREE_PAGE 0xE07F
+ #define TFBFCR_FREE_PAGE_BITS 0x07
+ #define TFBFCR_FREE_PAGE_LATCH BIT(6)
+ #define TFBFCR_SET_FREE_PAGE(x) (((x) & 0x3F) << TFBFCR_FREE_PAGE_BITS)
+ #define TFBFCR_TX_PAGE_SET BIT(13)
+ #define TFBFCR_MANU_ENTX BIT(15)
+ #define TX_FREEBUF_MASK 0x003F
+ #define TX_DPTSTART 0x4000
+
+#define P0_TSNR (0x12)
+ #define TXNR_TXB_ERR BIT(5)
+ #define TXNR_TXB_IDLE BIT(6)
+ #define TSNR_PKT_CNT(x) (((x) & 0x3F) << 8)
+ #define TXNR_TXB_REINIT BIT(14)
+ #define TSNR_TXB_START BIT(15)
+#define P0_RTDPR (0x14)
+#define P0_RXBCR1 (0x16)
+ #define RXBCR1_RXB_DISCARD BIT(14)
+ #define RXBCR1_RXB_START BIT(15)
+#define P0_RXBCR2 (0x18)
+ #define RXBCR2_PKT_MASK (0xFF)
+ #define RXBCR2_RXPC_MASK (0x7F)
+ #define RXBCR2_RXB_READY BIT(13)
+ #define RXBCR2_RXB_IDLE BIT(14)
+ #define RXBCR2_RXB_REINIT BIT(15)
+#define P0_RTWCR (0x1A)
+ #define RTWCR_RXWC_MASK (0x3FFF)
+ #define RTWCR_RX_LATCH BIT(15)
+#define P0_RCPHR (0x1C)
+
+ /* Definition of PAGE1 */
+#define P1_RPPER (0x22)
+ #define RPPER_RXEN BIT(0)
+#define P1_MRCR (0x28)
+#define P1_MDR (0x2A)
+#define P1_RMPR (0x2C)
+#define P1_TMPR (0x2E)
+#define P1_RXBSPCR (0x30)
+ #define RXBSPCR_STUF_WORD_CNT(x) (((x) & 0x7000) >> 12)
+ #define RXBSPCR_STUF_ENABLE BIT(15)
+#define P1_MCR (0x32)
+ #define MCR_SBP BIT(8)
+ #define MCR_SM BIT(9)
+ #define MCR_CRCENLAN BIT(11)
+ #define MCR_STP BIT(12)
+ /* Definition of PAGE2 */
+#define P2_CIR (0x42)
+#define P2_PCR (0x44)
+ #define PCR_POLL_EN BIT(0)
+ #define PCR_POLL_FLOWCTRL BIT(1)
+ #define PCR_POLL_BMCR BIT(2)
+ #define PCR_PHYID(x) ((x) << 8)
+#define P2_PHYSR (0x46)
+#define P2_MDIODR (0x48)
+#define P2_MDIOCR (0x4A)
+ #define MDIOCR_RADDR(x) ((x) & 0x1F)
+ #define MDIOCR_FADDR(x) (((x) & 0x1F) << 8)
+ #define MDIOCR_VALID BIT(13)
+ #define MDIOCR_READ BIT(14)
+ #define MDIOCR_WRITE BIT(15)
+#define P2_LCR0 (0x4C)
+ #define LCR_LED0_EN BIT(0)
+ #define LCR_LED0_100MODE BIT(1)
+ #define LCR_LED0_DUPLEX BIT(2)
+ #define LCR_LED0_LINK BIT(3)
+ #define LCR_LED0_ACT BIT(4)
+ #define LCR_LED0_COL BIT(5)
+ #define LCR_LED0_10MODE BIT(6)
+ #define LCR_LED0_DUPCOL BIT(7)
+ #define LCR_LED1_EN BIT(8)
+ #define LCR_LED1_100MODE BIT(9)
+ #define LCR_LED1_DUPLEX BIT(10)
+ #define LCR_LED1_LINK BIT(11)
+ #define LCR_LED1_ACT BIT(12)
+ #define LCR_LED1_COL BIT(13)
+ #define LCR_LED1_10MODE BIT(14)
+ #define LCR_LED1_DUPCOL BIT(15)
+#define P2_LCR1 (0x4E)
+ #define LCR_LED2_MASK (0xFF00)
+ #define LCR_LED2_EN BIT(0)
+ #define LCR_LED2_100MODE BIT(1)
+ #define LCR_LED2_DUPLEX BIT(2)
+ #define LCR_LED2_LINK BIT(3)
+ #define LCR_LED2_ACT BIT(4)
+ #define LCR_LED2_COL BIT(5)
+ #define LCR_LED2_10MODE BIT(6)
+ #define LCR_LED2_DUPCOL BIT(7)
+#define P2_IPGCR (0x50)
+#define P2_CRIR (0x52)
+#define P2_FLHWCR (0x54)
+#define P2_RXCR (0x56)
+ #define RXCR_PRO BIT(0)
+ #define RXCR_AMALL BIT(1)
+ #define RXCR_SEP BIT(2)
+ #define RXCR_AB BIT(3)
+ #define RXCR_AM BIT(4)
+ #define RXCR_AP BIT(5)
+ #define RXCR_ARP BIT(6)
+#define P2_JLCR (0x58)
+#define P2_MPLR (0x5C)
+
+ /* Definition of PAGE3 */
+#define P3_MACASR0 (0x62)
+ #define P3_MACASR(x) (P3_MACASR0 + 2 * (x))
+ #define MACASR_LOWBYTE_MASK 0x00FF
+ #define MACASR_HIGH_BITS 0x08
+#define P3_MACASR1 (0x64)
+#define P3_MACASR2 (0x66)
+#define P3_MFAR01 (0x68)
+#define P3_MFAR_BASE (0x68)
+ #define P3_MFAR(x) (P3_MFAR_BASE + 2 * (x))
+
+#define P3_MFAR23 (0x6A)
+#define P3_MFAR45 (0x6C)
+#define P3_MFAR67 (0x6E)
+#define P3_VID0FR (0x70)
+#define P3_VID1FR (0x72)
+#define P3_EECSR (0x74)
+#define P3_EEDR (0x76)
+#define P3_EECR (0x78)
+ #define EECR_ADDR_MASK (0x00FF)
+ #define EECR_READ_ACT BIT(8)
+ #define EECR_WRITE_ACT BIT(9)
+ #define EECR_WRITE_DISABLE BIT(10)
+ #define EECR_WRITE_ENABLE BIT(11)
+ #define EECR_EE_READY BIT(13)
+ #define EECR_RELOAD BIT(14)
+ #define EECR_RESET BIT(15)
+#define P3_TPCR (0x7A)
+ #define TPCR_PATT_MASK (0xFF)
+ #define TPCR_RAND_PKT_EN BIT(14)
+ #define TPCR_FIXED_PKT_EN BIT(15)
+#define P3_TPLR (0x7C)
+ /* Definition of PAGE4 */
+#define P4_SPICR (0x8A)
+ #define SPICR_RCEN BIT(0)
+ #define SPICR_QCEN BIT(1)
+ #define SPICR_RBRE BIT(3)
+ #define SPICR_PMM BIT(4)
+ #define SPICR_LOOPBACK BIT(8)
+ #define SPICR_CORE_RES_CLR BIT(10)
+ #define SPICR_SPI_RES_CLR BIT(11)
+#define P4_SPIISMR (0x8C)
+
+#define P4_COERCR0 (0x92)
+ #define COERCR0_RXIPCE BIT(0)
+ #define COERCR0_RXIPVE BIT(1)
+ #define COERCR0_RXV6PE BIT(2)
+ #define COERCR0_RXTCPE BIT(3)
+ #define COERCR0_RXUDPE BIT(4)
+ #define COERCR0_RXICMP BIT(5)
+ #define COERCR0_RXIGMP BIT(6)
+ #define COERCR0_RXICV6 BIT(7)
+
+ #define COERCR0_RXTCPV6 BIT(8)
+ #define COERCR0_RXUDPV6 BIT(9)
+ #define COERCR0_RXICMV6 BIT(10)
+ #define COERCR0_RXIGMV6 BIT(11)
+ #define COERCR0_RXICV6V6 BIT(12)
+
+ #define COERCR0_DEFAULT (COERCR0_RXIPCE | COERCR0_RXV6PE | \
+ COERCR0_RXTCPE | COERCR0_RXUDPE | \
+ COERCR0_RXTCPV6 | COERCR0_RXUDPV6)
+#define P4_COERCR1 (0x94)
+ #define COERCR1_IPCEDP BIT(0)
+ #define COERCR1_IPVEDP BIT(1)
+ #define COERCR1_V6VEDP BIT(2)
+ #define COERCR1_TCPEDP BIT(3)
+ #define COERCR1_UDPEDP BIT(4)
+ #define COERCR1_ICMPDP BIT(5)
+ #define COERCR1_IGMPDP BIT(6)
+ #define COERCR1_ICV6DP BIT(7)
+ #define COERCR1_RX64TE BIT(8)
+ #define COERCR1_RXPPPE BIT(9)
+ #define COERCR1_TCP6DP BIT(10)
+ #define COERCR1_UDP6DP BIT(11)
+ #define COERCR1_IC6DP BIT(12)
+ #define COERCR1_IG6DP BIT(13)
+ #define COERCR1_ICV66DP BIT(14)
+ #define COERCR1_RPCE BIT(15)
+
+ #define COERCR1_DEFAULT (COERCR1_RXPPPE)
+
+#define P4_COETCR0 (0x96)
+ #define COETCR0_TXIP BIT(0)
+ #define COETCR0_TXTCP BIT(1)
+ #define COETCR0_TXUDP BIT(2)
+ #define COETCR0_TXICMP BIT(3)
+ #define COETCR0_TXIGMP BIT(4)
+ #define COETCR0_TXICV6 BIT(5)
+ #define COETCR0_TXTCPV6 BIT(8)
+ #define COETCR0_TXUDPV6 BIT(9)
+ #define COETCR0_TXICMV6 BIT(10)
+ #define COETCR0_TXIGMV6 BIT(11)
+ #define COETCR0_TXICV6V6 BIT(12)
+
+ #define COETCR0_DEFAULT (COETCR0_TXIP | COETCR0_TXTCP | \
+ COETCR0_TXUDP | COETCR0_TXTCPV6 | \
+ COETCR0_TXUDPV6)
+#define P4_COETCR1 (0x98)
+ #define COETCR1_TX64TE BIT(0)
+ #define COETCR1_TXPPPE BIT(1)
+
+#define P4_COECEDR (0x9A)
+#define P4_L2CECR (0x9C)
+
+ /* Definition of PAGE5 */
+#define P5_WFTR (0xA2)
+ #define WFTR_2MS (0x01)
+ #define WFTR_4MS (0x02)
+ #define WFTR_8MS (0x03)
+ #define WFTR_16MS (0x04)
+ #define WFTR_32MS (0x05)
+ #define WFTR_64MS (0x06)
+ #define WFTR_128MS (0x07)
+ #define WFTR_256MS (0x08)
+ #define WFTR_512MS (0x09)
+ #define WFTR_1024MS (0x0A)
+ #define WFTR_2048MS (0x0B)
+ #define WFTR_4096MS (0x0C)
+ #define WFTR_8192MS (0x0D)
+ #define WFTR_16384MS (0x0E)
+ #define WFTR_32768MS (0x0F)
+#define P5_WFCCR (0xA4)
+#define P5_WFCR03 (0xA6)
+ #define WFCR03_F0_EN BIT(0)
+ #define WFCR03_F1_EN BIT(4)
+ #define WFCR03_F2_EN BIT(8)
+ #define WFCR03_F3_EN BIT(12)
+#define P5_WFCR47 (0xA8)
+ #define WFCR47_F4_EN BIT(0)
+ #define WFCR47_F5_EN BIT(4)
+ #define WFCR47_F6_EN BIT(8)
+ #define WFCR47_F7_EN BIT(12)
+#define P5_WF0BMR0 (0xAA)
+#define P5_WF0BMR1 (0xAC)
+#define P5_WF0CR (0xAE)
+#define P5_WF0OBR (0xB0)
+#define P5_WF1BMR0 (0xB2)
+#define P5_WF1BMR1 (0xB4)
+#define P5_WF1CR (0xB6)
+#define P5_WF1OBR (0xB8)
+#define P5_WF2BMR0 (0xBA)
+#define P5_WF2BMR1 (0xBC)
+
+ /* Definition of PAGE6 */
+#define P6_WF2CR (0xC2)
+#define P6_WF2OBR (0xC4)
+#define P6_WF3BMR0 (0xC6)
+#define P6_WF3BMR1 (0xC8)
+#define P6_WF3CR (0xCA)
+#define P6_WF3OBR (0xCC)
+#define P6_WF4BMR0 (0xCE)
+#define P6_WF4BMR1 (0xD0)
+#define P6_WF4CR (0xD2)
+#define P6_WF4OBR (0xD4)
+#define P6_WF5BMR0 (0xD6)
+#define P6_WF5BMR1 (0xD8)
+#define P6_WF5CR (0xDA)
+#define P6_WF5OBR (0xDC)
+
+/* Definition of PAGE7 */
+#define P7_WF6BMR0 (0xE2)
+#define P7_WF6BMR1 (0xE4)
+#define P7_WF6CR (0xE6)
+#define P7_WF6OBR (0xE8)
+#define P7_WF7BMR0 (0xEA)
+#define P7_WF7BMR1 (0xEC)
+#define P7_WF7CR (0xEE)
+#define P7_WF7OBR (0xF0)
+#define P7_WFR01 (0xF2)
+#define P7_WFR23 (0xF4)
+#define P7_WFR45 (0xF6)
+#define P7_WFR67 (0xF8)
+#define P7_WFPC0 (0xFA)
+#define P7_WFPC1 (0xFC)
+
+/* Tx headers structure */
+struct tx_sop_header {
+ /* bit 15-11: flags, bit 10-0: packet length */
+ u16 flags_len;
+ /* bit 15-11: sequence number, bit 11-0: packet length bar */
+ u16 seq_lenbar;
+} __packed;
+
+struct tx_segment_header {
+ /* bit 15-14: flags, bit 13-11: segment number */
+ /* bit 10-0: segment length */
+ u16 flags_seqnum_seglen;
+ /* bit 15-14: end offset, bit 13-11: start offset */
+ /* bit 10-0: segment length bar */
+ u16 eo_so_seglenbar;
+} __packed;
+
+struct tx_eop_header {
+ /* bit 15-11: sequence number, bit 10-0: packet length */
+ u16 seq_len;
+ /* bit 15-11: sequence number bar, bit 10-0: packet length bar */
+ u16 seqbar_lenbar;
+} __packed;
+
+struct tx_pkt_info {
+ struct tx_sop_header sop;
+ struct tx_segment_header seg;
+ struct tx_eop_header eop;
+ u16 pkt_len;
+ u16 seq_num;
+} __packed;
+
+/* Rx headers structure */
+struct rx_header {
+ u16 flags_len;
+ u16 seq_lenbar;
+ u16 flags;
+} __packed;
+
+extern unsigned long ax88796c_no_regs_mask[];
+
+#endif /* #ifndef _AX88796C_MAIN_H */
diff --git a/drivers/net/ethernet/asix/ax88796c_spi.c b/drivers/net/ethernet/asix/ax88796c_spi.c
new file mode 100644
index 000000000000..829494e624c5
--- /dev/null
+++ b/drivers/net/ethernet/asix/ax88796c_spi.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2010 ASIX Electronics Corporation
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * ASIX AX88796C SPI Fast Ethernet Linux driver
+ */
+
+#define pr_fmt(fmt) "ax88796c: " fmt
+
+#include <linux/string.h>
+#include <linux/spi/spi.h>
+
+#include "ax88796c_spi.h"
+
+const u8 ax88796c_rx_cmd_buf[5] = {AX_SPICMD_READ_RXQ, 0xFF, 0xFF, 0xFF, 0xFF};
+const u8 ax88796c_tx_cmd_buf[4] = {AX_SPICMD_WRITE_TXQ, 0xFF, 0xFF, 0xFF};
+
+/* driver bus management functions */
+int axspi_wakeup(struct axspi_data *ax_spi)
+{
+ int ret;
+
+ ax_spi->cmd_buf[0] = AX_SPICMD_EXIT_PWD; /* OP */
+ ret = spi_write(ax_spi->spi, ax_spi->cmd_buf, 1);
+ if (ret)
+ dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
+ return ret;
+}
+
+int axspi_read_status(struct axspi_data *ax_spi, struct spi_status *status)
+{
+ int ret;
+
+ /* OP */
+ ax_spi->cmd_buf[0] = AX_SPICMD_READ_STATUS;
+ ret = spi_write_then_read(ax_spi->spi, ax_spi->cmd_buf, 1, (u8 *)&status, 3);
+ if (ret)
+ dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
+ else
+ le16_to_cpus(&status->isr);
+
+ return ret;
+}
+
+int axspi_read_rxq(struct axspi_data *ax_spi, void *data, int len)
+{
+ struct spi_transfer *xfer = ax_spi->spi_rx_xfer;
+ int ret;
+
+ memcpy(ax_spi->cmd_buf, ax88796c_rx_cmd_buf, 5);
+
+ xfer->tx_buf = ax_spi->cmd_buf;
+ xfer->rx_buf = NULL;
+ xfer->len = ax_spi->comp ? 2 : 5;
+ xfer->bits_per_word = 8;
+ spi_message_add_tail(xfer, &ax_spi->rx_msg);
+
+ xfer++;
+ xfer->rx_buf = data;
+ xfer->tx_buf = NULL;
+ xfer->len = len;
+ xfer->bits_per_word = 8;
+ spi_message_add_tail(xfer, &ax_spi->rx_msg);
+ ret = spi_sync(ax_spi->spi, &ax_spi->rx_msg);
+ if (ret)
+ dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
+
+ return ret;
+}
+
+int axspi_write_txq(const struct axspi_data *ax_spi, void *data, int len)
+{
+ return spi_write(ax_spi->spi, data, len);
+}
+
+u16 axspi_read_reg(struct axspi_data *ax_spi, u8 reg)
+{
+ int ret;
+ int len = ax_spi->comp ? 3 : 4;
+
+ ax_spi->cmd_buf[0] = 0x03; /* OP code read register */
+ ax_spi->cmd_buf[1] = reg; /* register address */
+ ax_spi->cmd_buf[2] = 0xFF; /* dumy cycle */
+ ax_spi->cmd_buf[3] = 0xFF; /* dumy cycle */
+ ret = spi_write_then_read(ax_spi->spi,
+ ax_spi->cmd_buf, len,
+ ax_spi->rx_buf, 2);
+ if (ret)
+ dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
+ else
+ le16_to_cpus(ax_spi->rx_buf);
+
+ return *(u16 *)ax_spi->rx_buf;
+}
+
+int axspi_write_reg(struct axspi_data *ax_spi, u8 reg, u16 value)
+{
+ int ret;
+
+ memset(ax_spi->cmd_buf, 0, sizeof(ax_spi->cmd_buf));
+ ax_spi->cmd_buf[0] = AX_SPICMD_WRITE_REG; /* OP code read register */
+ ax_spi->cmd_buf[1] = reg; /* register address */
+ ax_spi->cmd_buf[2] = value;
+ ax_spi->cmd_buf[3] = value >> 8;
+
+ ret = spi_write(ax_spi->spi, ax_spi->cmd_buf, 4);
+ if (ret)
+ dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
+ return ret;
+}
+
diff --git a/drivers/net/ethernet/asix/ax88796c_spi.h b/drivers/net/ethernet/asix/ax88796c_spi.h
new file mode 100644
index 000000000000..5bcf91f603fb
--- /dev/null
+++ b/drivers/net/ethernet/asix/ax88796c_spi.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2010 ASIX Electronics Corporation
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * ASIX AX88796C SPI Fast Ethernet Linux driver
+ */
+
+#ifndef _AX88796C_SPI_H
+#define _AX88796C_SPI_H
+
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+/* Definition of SPI command */
+#define AX_SPICMD_WRITE_TXQ 0x02
+#define AX_SPICMD_READ_REG 0x03
+#define AX_SPICMD_READ_STATUS 0x05
+#define AX_SPICMD_READ_RXQ 0x0B
+#define AX_SPICMD_BIDIR_WRQ 0xB2
+#define AX_SPICMD_WRITE_REG 0xD8
+#define AX_SPICMD_EXIT_PWD 0xAB
+
+extern const u8 ax88796c_rx_cmd_buf[];
+extern const u8 ax88796c_tx_cmd_buf[];
+
+struct axspi_data {
+ struct spi_device *spi;
+ struct spi_message rx_msg;
+ struct spi_transfer spi_rx_xfer[2];
+ u8 cmd_buf[6];
+ u8 rx_buf[6];
+ u8 comp;
+};
+
+struct spi_status {
+ u16 isr;
+ u8 status;
+# define AX_STATUS_READY 0x80
+};
+
+int axspi_read_rxq(struct axspi_data *ax_spi, void *data, int len);
+int axspi_write_txq(const struct axspi_data *ax_spi, void *data, int len);
+u16 axspi_read_reg(struct axspi_data *ax_spi, u8 reg);
+int axspi_write_reg(struct axspi_data *ax_spi, u8 reg, u16 value);
+int axspi_read_status(struct axspi_data *ax_spi, struct spi_status *status);
+int axspi_wakeup(struct axspi_data *ax_spi);
+
+static inline u16 AX_READ(struct axspi_data *ax_spi, u8 offset)
+{
+ return axspi_read_reg(ax_spi, offset);
+}
+
+static inline int AX_WRITE(struct axspi_data *ax_spi, u16 value, u8 offset)
+{
+ return axspi_write_reg(ax_spi, offset, value);
+}
+
+static inline int AX_READ_STATUS(struct axspi_data *ax_spi,
+ struct spi_status *status)
+{
+ return axspi_read_status(ax_spi, status);
+}
+
+static inline int AX_WAKEUP(struct axspi_data *ax_spi)
+{
+ return axspi_wakeup(ax_spi);
+}
+#endif
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 9ca87bc73c44..f7621c3a938e 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -221,6 +221,7 @@ enum tunable_id {
ETHTOOL_RX_COPYBREAK,
ETHTOOL_TX_COPYBREAK,
ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */
+ ETHTOOL_SPI_COMPRESSION, /* SPI transfer compression */
/*
* Add your fresh new tunable attribute above and remember to update
* tunable_strings[] in net/core/ethtool.c
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 24036e3055a1..bd49f83d7daa 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -83,6 +83,7 @@ tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
[ETHTOOL_RX_COPYBREAK] = "rx-copybreak",
[ETHTOOL_TX_COPYBREAK] = "tx-copybreak",
[ETHTOOL_PFC_PREVENTION_TOUT] = "pfc-prevention-tout",
+ [ETHTOOL_SPI_COMPRESSION] = "spi-compression",
};
const char
--
2.26.2
^ permalink raw reply related
* [PATCH v7 2/3] dt-bindings: net: Add bindings for AX88796C SPI Ethernet Adapter
From: Łukasz Stelmach @ 2020-11-24 12:03 UTC (permalink / raw)
To: Andrew Lunn, jim.cromie, Heiner Kallweit, David S. Miller,
Jakub Kicinski, Rob Herring, Kukjin Kim, Krzysztof Kozlowski,
Russell King, netdev, devicetree, linux-kernel, linux-arm-kernel,
linux-samsung-soc
Cc: Bartłomiej Żolnierkiewicz, Marek Szyprowski,
Łukasz Stelmach, Rob Herring
In-Reply-To: <20201124120330.32445-1-l.stelmach@samsung.com>
Add bindings for AX88796C SPI Ethernet Adapter.
Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
---
.../bindings/net/asix,ax88796c.yaml | 73 +++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/asix,ax88796c.yaml
diff --git a/Documentation/devicetree/bindings/net/asix,ax88796c.yaml b/Documentation/devicetree/bindings/net/asix,ax88796c.yaml
new file mode 100644
index 000000000000..699ebf452479
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/asix,ax88796c.yaml
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/asix,ax88796c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASIX AX88796C SPI Ethernet Adapter
+
+maintainers:
+ - Łukasz Stelmach <l.stelmach@samsung.com>
+
+description: |
+ ASIX AX88796C is an Ethernet controller with a built in PHY. This
+ describes SPI mode of the chip.
+
+ The node for this driver must be a child node of an SPI controller,
+ hence all mandatory properties described in
+ ../spi/spi-controller.yaml must be specified.
+
+allOf:
+ - $ref: ethernet-controller.yaml#
+
+properties:
+ compatible:
+ const: asix,ax88796c
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 40000000
+
+ interrupts:
+ maxItems: 1
+
+ reset-gpios:
+ description:
+ A GPIO line handling reset of the chip. As the line is active low,
+ it should be marked GPIO_ACTIVE_LOW.
+ maxItems: 1
+
+ local-mac-address: true
+
+ mac-address: true
+
+required:
+ - compatible
+ - reg
+ - spi-max-frequency
+ - interrupts
+ - reset-gpios
+
+additionalProperties: false
+
+examples:
+ # Artik5 eval board
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/gpio/gpio.h>
+ spi0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet@0 {
+ compatible = "asix,ax88796c";
+ reg = <0x0>;
+ local-mac-address = [00 00 00 00 00 00]; /* Filled in by a bootloader */
+ interrupt-parent = <&gpx2>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ spi-max-frequency = <40000000>;
+ reset-gpios = <&gpe0 2 GPIO_ACTIVE_LOW>;
+ };
+ };
--
2.26.2
^ permalink raw reply related
* Re: [PATCH net-next 2/2] net: nfc: s3fwrn5: Support a UART interface
From: Bongsu Jeon @ 2020-11-24 12:05 UTC (permalink / raw)
To: krzk@kernel.org
Cc: Bongsu Jeon, kuba@kernel.org, linux-kernel@vger.kernel.org,
linux-nfc@lists.01.org, netdev@vger.kernel.org
In-Reply-To: <20201123081940.GA9323@kozik-lap>
On Mon, Nov 23, 2020 at 5:55 PM krzk@kernel.org <krzk@kernel.org> wrote:
>
> On Mon, Nov 23, 2020 at 04:56:58PM +0900, Bongsu Jeon wrote:
> > Since S3FWRN82 NFC Chip, The UART interface can be used.
> > S3FWRN82 uses NCI protocol and supports I2C and UART interface.
> >
> > Signed-off-by: Bongsu Jeon <bongsu.jeon@samsung.com>
>
> Please start sending emails properly, e.g. with git send-email, so all
> your patches in the patchset are referencing the first patch.
>
Ok. I will do that.
> > ---
> > drivers/nfc/s3fwrn5/Kconfig | 12 ++
> > drivers/nfc/s3fwrn5/Makefile | 2 +
> > drivers/nfc/s3fwrn5/uart.c | 250 +++++++++++++++++++++++++++++++++++
> > 3 files changed, 264 insertions(+)
> > create mode 100644 drivers/nfc/s3fwrn5/uart.c
> >
> > diff --git a/drivers/nfc/s3fwrn5/Kconfig b/drivers/nfc/s3fwrn5/Kconfig
> > index 3f8b6da58280..6f88737769e1 100644
> > --- a/drivers/nfc/s3fwrn5/Kconfig
> > +++ b/drivers/nfc/s3fwrn5/Kconfig
> > @@ -20,3 +20,15 @@ config NFC_S3FWRN5_I2C
> > To compile this driver as a module, choose m here. The module will
> > be called s3fwrn5_i2c.ko.
> > Say N if unsure.
> > +
> > +config NFC_S3FWRN82_UART
> > + tristate "Samsung S3FWRN82 UART support"
> > + depends on NFC_NCI && SERIAL_DEV_BUS
>
> What about SERIAL_DEV_BUS as module? Shouldn't this be
> SERIAL_DEV_BUS || !SERIAL_DEV_BUS?
>
SERIAL_DEV_BUS is okay even if SERIAL_DEV_BUS is defined as module.
> > + select NFC_S3FWRN5
> > + help
> > + This module adds support for a UART interface to the S3FWRN82 chip.
> > + Select this if your platform is using the UART bus.
> > +
> > + To compile this driver as a module, choose m here. The module will
> > + be called s3fwrn82_uart.ko.
> > + Say N if unsure.
> > diff --git a/drivers/nfc/s3fwrn5/Makefile b/drivers/nfc/s3fwrn5/Makefile
> > index d0ffa35f50e8..d1902102060b 100644
> > --- a/drivers/nfc/s3fwrn5/Makefile
> > +++ b/drivers/nfc/s3fwrn5/Makefile
> > @@ -5,6 +5,8 @@
> >
> > s3fwrn5-objs = core.o firmware.o nci.o
> > s3fwrn5_i2c-objs = i2c.o
> > +s3fwrn82_uart-objs = uart.o
> >
> > obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5.o
> > obj-$(CONFIG_NFC_S3FWRN5_I2C) += s3fwrn5_i2c.o
> > +obj-$(CONFIG_NFC_S3FWRN82_UART) += s3fwrn82_uart.o
> > diff --git a/drivers/nfc/s3fwrn5/uart.c b/drivers/nfc/s3fwrn5/uart.c
> > new file mode 100644
> > index 000000000000..b3c36a5b28d3
> > --- /dev/null
> > +++ b/drivers/nfc/s3fwrn5/uart.c
> > @@ -0,0 +1,250 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * UART Link Layer for S3FWRN82 NCI based Driver
> > + *
> > + * Copyright (C) 2020 Samsung Electronics
> > + * Author: Bongsu Jeon <bongsu.jeon@samsung.com>
>
> You copied a lot from existing i2c.c. Please keep also the original
> copyrights.
>
Okay. I will keep also the original copyrights.
> > + * All rights reserved.
> > + */
> > +
> > +#include <linux/device.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/nfc.h>
> > +#include <linux/netdevice.h>
> > +#include <linux/of.h>
> > +#include <linux/serdev.h>
> > +#include <linux/gpio.h>
> > +#include <linux/of_gpio.h>
> > +
> > +#include "s3fwrn5.h"
> > +
> > +#define S3FWRN82_UART_DRIVER_NAME "s3fwrn82_uart"
>
> Remove the define, it is used only once.
>
> > +#define S3FWRN82_NCI_HEADER 3
> > +#define S3FWRN82_NCI_IDX 2
> > +#define S3FWRN82_EN_WAIT_TIME 20
> > +#define NCI_SKB_BUFF_LEN 258
> > +
> > +struct s3fwrn82_uart_phy {
> > + struct serdev_device *ser_dev;
> > + struct nci_dev *ndev;
> > + struct sk_buff *recv_skb;
> > +
> > + unsigned int gpio_en;
> > + unsigned int gpio_fw_wake;
> > +
> > + /* mutex is used to synchronize */
>
> Please do not write obvious comments. Mutex is always used to
> synchronize, what else is it for? Instead you must describe what exactly
> is protected with mutex.
>
I understand it. I will fix it.
> > + struct mutex mutex;
> > + enum s3fwrn5_mode mode;
> > +};
> > +
> > +static void s3fwrn82_uart_set_wake(void *phy_id, bool wake)
> > +{
> > + struct s3fwrn82_uart_phy *phy = phy_id;
> > +
> > + mutex_lock(&phy->mutex);
> > + gpio_set_value(phy->gpio_fw_wake, wake);
> > + msleep(S3FWRN82_EN_WAIT_TIME);
> > + mutex_unlock(&phy->mutex);
> > +}
> > +
> > +static void s3fwrn82_uart_set_mode(void *phy_id, enum s3fwrn5_mode mode)
> > +{
> > + struct s3fwrn82_uart_phy *phy = phy_id;
> > +
> > + mutex_lock(&phy->mutex);
> > + if (phy->mode == mode)
> > + goto out;
> > + phy->mode = mode;
> > + gpio_set_value(phy->gpio_en, 1);
> > + gpio_set_value(phy->gpio_fw_wake, 0);
> > + if (mode == S3FWRN5_MODE_FW)
> > + gpio_set_value(phy->gpio_fw_wake, 1);
> > + if (mode != S3FWRN5_MODE_COLD) {
> > + msleep(S3FWRN82_EN_WAIT_TIME);
> > + gpio_set_value(phy->gpio_en, 0);
> > + msleep(S3FWRN82_EN_WAIT_TIME);
> > + }
> > +out:
> > + mutex_unlock(&phy->mutex);
> > +}
> > +
> > +static enum s3fwrn5_mode s3fwrn82_uart_get_mode(void *phy_id)
> > +{
> > + struct s3fwrn82_uart_phy *phy = phy_id;
> > + enum s3fwrn5_mode mode;
> > +
> > + mutex_lock(&phy->mutex);
> > + mode = phy->mode;
> > + mutex_unlock(&phy->mutex);
> > + return mode;
> > +}
>
> All this duplicates I2C version. You need to start either reusing common
> blocks.
>
Okay. I will do refactoring on i2c.c and uart.c to make common blocks.
is it okay to separate a patch for it?
> > +
> > +static int s3fwrn82_uart_write(void *phy_id, struct sk_buff *out)
> > +{
> > + struct s3fwrn82_uart_phy *phy = phy_id;
> > + int err;
> > +
> > + err = serdev_device_write(phy->ser_dev,
> > + out->data, out->len,
> > + MAX_SCHEDULE_TIMEOUT);
> > + if (err < 0)
> > + return err;
> > +
> > + return 0;
> > +}
> > +
> > +static const struct s3fwrn5_phy_ops uart_phy_ops = {
> > + .set_wake = s3fwrn82_uart_set_wake,
> > + .set_mode = s3fwrn82_uart_set_mode,
> > + .get_mode = s3fwrn82_uart_get_mode,
> > + .write = s3fwrn82_uart_write,
> > +};
> > +
> > +static int s3fwrn82_uart_read(struct serdev_device *serdev,
> > + const unsigned char *data,
> > + size_t count)
> > +{
> > + struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
> > + size_t i;
> > +
> > + for (i = 0; i < count; i++) {
> > + skb_put_u8(phy->recv_skb, *data++);
> > +
> > + if (phy->recv_skb->len < S3FWRN82_NCI_HEADER)
> > + continue;
> > +
> > + if ((phy->recv_skb->len - S3FWRN82_NCI_HEADER)
> > + < phy->recv_skb->data[S3FWRN82_NCI_IDX])
> > + continue;
> > +
> > + s3fwrn5_recv_frame(phy->ndev, phy->recv_skb, phy->mode);
> > + phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
> > + if (!phy->recv_skb)
> > + return 0;
> > + }
> > +
> > + return i;
> > +}
> > +
> > +static struct serdev_device_ops s3fwrn82_serdev_ops = {
>
> const
>
> > + .receive_buf = s3fwrn82_uart_read,
> > + .write_wakeup = serdev_device_write_wakeup,
> > +};
> > +
> > +static const struct of_device_id s3fwrn82_uart_of_match[] = {
> > + { .compatible = "samsung,s3fwrn82-uart", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match);
> > +
> > +static int s3fwrn82_uart_parse_dt(struct serdev_device *serdev)
> > +{
> > + struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
> > + struct device_node *np = serdev->dev.of_node;
> > +
> > + if (!np)
> > + return -ENODEV;
> > +
> > + phy->gpio_en = of_get_named_gpio(np, "en-gpios", 0);
> > + if (!gpio_is_valid(phy->gpio_en))
> > + return -ENODEV;
> > +
> > + phy->gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
>
> You should not cast it it unsigned int. I'll fix the s3fwrn5 from which
> you copied this apparently.
>
Okay. I will fix it.
> > + if (!gpio_is_valid(phy->gpio_fw_wake))
> > + return -ENODEV;
> > +
> > + return 0;
> > +}
> > +
> > +static int s3fwrn82_uart_probe(struct serdev_device *serdev)
> > +{
> > + struct s3fwrn82_uart_phy *phy;
> > + int ret = -ENOMEM;
> > +
> > + phy = devm_kzalloc(&serdev->dev, sizeof(*phy), GFP_KERNEL);
> > + if (!phy)
> > + goto err_exit;
> > +
> > + phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
> > + if (!phy->recv_skb)
> > + goto err_free;
> > +
> > + mutex_init(&phy->mutex);
> > + phy->mode = S3FWRN5_MODE_COLD;
> > +
> > + phy->ser_dev = serdev;
> > + serdev_device_set_drvdata(serdev, phy);
> > + serdev_device_set_client_ops(serdev, &s3fwrn82_serdev_ops);
> > + ret = serdev_device_open(serdev);
> > + if (ret) {
> > + dev_err(&serdev->dev, "Unable to open device\n");
> > + goto err_skb;
> > + }
> > +
> > + ret = serdev_device_set_baudrate(serdev, 115200);
>
> Why baudrate is fixed?
>
RN82 NFC chip only supports 115200 baudrate for UART.
> > + if (ret != 115200) {
> > + ret = -EINVAL;
> > + goto err_serdev;
> > + }
> > +
> > + serdev_device_set_flow_control(serdev, false);
> > +
> > + ret = s3fwrn82_uart_parse_dt(serdev);
> > + if (ret < 0)
> > + goto err_serdev;
> > +
> > + ret = devm_gpio_request_one(&phy->ser_dev->dev,
> > + phy->gpio_en,
> > + GPIOF_OUT_INIT_HIGH,
> > + "s3fwrn82_en");
>
> This is weirdly wrapped.
>
Did you ask about devem_gpio_request_one function's parenthesis and parameters?
If it is right, I changed it after i ran the checkpatch.pl --strict and
i saw message like the alignment should match open parenthesis.
> > + if (ret < 0)
> > + goto err_serdev;
> > +
> > + ret = devm_gpio_request_one(&phy->ser_dev->dev,
> > + phy->gpio_fw_wake,
> > + GPIOF_OUT_INIT_LOW,
> > + "s3fwrn82_fw_wake");
> > + if (ret < 0)
> > + goto err_serdev;
> > +
> > + ret = s3fwrn5_probe(&phy->ndev, phy, &phy->ser_dev->dev, &uart_phy_ops);
> > + if (ret < 0)
> > + goto err_serdev;
> > +
> > + return ret;
> > +
> > +err_serdev:
> > + serdev_device_close(serdev);
> > +err_skb:
> > + kfree_skb(phy->recv_skb);
> > +err_free:
> > + kfree(phy);
>
> Eee.... why? Did you test this code?
>
I didn't test this code. i just added this code as defense code.
If the error happens, then allocated memory and device will be free
according to the fail case.
> > +err_exit:
> > + return ret;
> > +}
> > +
> > +static void s3fwrn82_uart_remove(struct serdev_device *serdev)
> > +{
> > + struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
> > +
> > + s3fwrn5_remove(phy->ndev);
> > + serdev_device_close(serdev);
> > + kfree_skb(phy->recv_skb);
> > + kfree(phy);
>
> This does not look like tested...
>
I tested this code using unbind of the serial device.
It worked and I saw the debugging log that i added for checking the
code to be sure.
> Best regards,
> Krzysztof
^ permalink raw reply
* Re: [PATCH iproute2-next v2] tc flower: use right ethertype in icmp/arp parsing
From: Roi Dayan @ 2020-11-24 12:13 UTC (permalink / raw)
To: David Ahern, Zahari Doychev, netdev; +Cc: simon.horman, jhs, jianbol
In-Reply-To: <3ae696c9-b4dd-a2e5-77d5-c572e98a4000@nvidia.com>
On 2020-11-24 11:39 AM, Roi Dayan wrote:
>
>
> On 2020-11-14 5:12 AM, David Ahern wrote:
>> On 11/10/20 12:53 AM, Zahari Doychev wrote:
>>> Currently the icmp and arp parsing functions are called with incorrect
>>> ethtype in case of vlan or cvlan filter options. In this case either
>>> cvlan_ethtype or vlan_ethtype has to be used. The ethtype is now updated
>>> each time a vlan ethtype is matched during parsing.
>>>
>>> Signed-off-by: Zahari Doychev <zahari.doychev@linux.com>
>>> ---
>>> tc/f_flower.c | 52 +++++++++++++++++++++++----------------------------
>>> 1 file changed, 23 insertions(+), 29 deletions(-)
>>>
>>
>> Thanks, looks much better.
>>
>> applied to iproute2-next.
>>
>
> Hi,
>
> I didn't debug yet but with this commit I am failing to add a tc
> rule I always could before. also the error msg doesn't make sense.
>
> Example:
>
> # tc filter add dev enp8s0f0 protocol 802.1Q parent ffff: prio 1 flower\
> skip_hw dst_mac e4:11:22:11:4a:51 src_mac e4:11:22:11:4a:50\
> vlan_ethtype 0x800 vlan_id 100 vlan_prio 0 action vlan pop action\
> mirred egress redirect dev enp8s0f0_0
>
>
> Can't set "vlan_id" if ethertype isn't 802.1Q or 802.1AD
>
>
> I used protocol 802.1Q and vlan_ethtype 0x800.
> am i missing something? the rule should look different now?
>
> Thanks,
> Roi
Hi,
I debugged this and it break vlan rules.
The issue is from this part of the change
@@ -1464,6 +1464,8 @@ static int flower_parse_opt(struct filter_util
*qu, char *handle,
&vlan_ethtype, n);
if (ret < 0)
return -1;
+ /* get new ethtype for later parsing */
+ eth_type = vlan_ethtype;
Now params vlan_id, vlan_prio check if eth_type is vlan but it's not.
it's 0x0800 as the example as it was set to the vlan_ethtype.
Need to continue check the outer, so tc_proto.
i'll prep a fix commit for review.
Thanks,
Roi
^ permalink raw reply
* Re: [PATCH v7 3/3] net: ax88796c: ASIX AX88796C SPI Ethernet Adapter Driver
From: Krzysztof Kozlowski @ 2020-11-24 12:17 UTC (permalink / raw)
To: Łukasz Stelmach
Cc: Andrew Lunn, jim.cromie, Heiner Kallweit, David S. Miller,
Jakub Kicinski, Rob Herring, Kukjin Kim, Russell King, netdev,
devicetree, linux-kernel, linux-arm-kernel, linux-samsung-soc,
Bartłomiej Żolnierkiewicz, Marek Szyprowski
In-Reply-To: <20201124120330.32445-4-l.stelmach@samsung.com>
On Tue, Nov 24, 2020 at 01:03:30PM +0100, Łukasz Stelmach wrote:
> ASIX AX88796[1] is a versatile ethernet adapter chip, that can be
> connected to a CPU with a 8/16-bit bus or with an SPI. This driver
> supports SPI connection.
>
> The driver has been ported from the vendor kernel for ARTIK5[2]
> boards. Several changes were made to adapt it to the current kernel
> which include:
>
> + updated DT configuration,
> + clock configuration moved to DT,
> + new timer, ethtool and gpio APIs,
> + dev_* instead of pr_* and custom printk() wrappers,
> + removed awkward vendor power managemtn.
> + introduced ethtool tunable to control SPI compression
>
> [1] https://www.asix.com.tw/products.php?op=pItemdetail&PItemID=104;65;86&PLine=65
> [2] https://git.tizen.org/cgit/profile/common/platform/kernel/linux-3.10-artik/
>
> The other ax88796 driver is for NE2000 compatible AX88796L chip. These
> chips are not compatible. Hence, two separate drivers are required.
>
> Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
> ---
> MAINTAINERS | 6 +
> drivers/net/ethernet/Kconfig | 1 +
> drivers/net/ethernet/Makefile | 1 +
> drivers/net/ethernet/asix/Kconfig | 35 +
> drivers/net/ethernet/asix/Makefile | 6 +
> drivers/net/ethernet/asix/ax88796c_ioctl.c | 221 ++++
> drivers/net/ethernet/asix/ax88796c_ioctl.h | 26 +
> drivers/net/ethernet/asix/ax88796c_main.c | 1132 ++++++++++++++++++++
> drivers/net/ethernet/asix/ax88796c_main.h | 561 ++++++++++
> drivers/net/ethernet/asix/ax88796c_spi.c | 112 ++
> drivers/net/ethernet/asix/ax88796c_spi.h | 69 ++
> include/uapi/linux/ethtool.h | 1 +
> net/ethtool/common.c | 1 +
> 13 files changed, 2172 insertions(+)
> create mode 100644 drivers/net/ethernet/asix/Kconfig
> create mode 100644 drivers/net/ethernet/asix/Makefile
> create mode 100644 drivers/net/ethernet/asix/ax88796c_ioctl.c
> create mode 100644 drivers/net/ethernet/asix/ax88796c_ioctl.h
> create mode 100644 drivers/net/ethernet/asix/ax88796c_main.c
> create mode 100644 drivers/net/ethernet/asix/ax88796c_main.h
> create mode 100644 drivers/net/ethernet/asix/ax88796c_spi.c
> create mode 100644 drivers/net/ethernet/asix/ax88796c_spi.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 14b8ec0bb58b..930dc859d4f7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2812,6 +2812,12 @@ S: Maintained
> F: Documentation/hwmon/asc7621.rst
> F: drivers/hwmon/asc7621.c
>
> +ASIX AX88796C SPI ETHERNET ADAPTER
> +M: Łukasz Stelmach <l.stelmach@samsung.com>
> +S: Maintained
> +F: Documentation/devicetree/bindings/net/asix,ax99706c-spi.yaml
Wrong file name.
Best regards,
Krzysztof
> +F: drivers/net/ethernet/asix/ax88796c_*
> +
^ permalink raw reply
* [PATCH] net/af_unix: don't create a path for a binded socket
From: Denis Kirjanov @ 2020-11-24 12:24 UTC (permalink / raw)
To: netdev; +Cc: kuba, davem
in the case of the socket which is bound to an adress
there is no sense to create a path in the next attempts
here is a program that shows the issue:
int main()
{
int s;
struct sockaddr_un a;
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s<0)
perror("socket() failed\n");
printf("First bind()\n");
memset(&a, 0, sizeof(a));
a.sun_family = AF_UNIX;
strncpy(a.sun_path, "/tmp/.first_bind", sizeof(a.sun_path));
if ((bind(s, (const struct sockaddr*) &a, sizeof(a))) == -1)
perror("bind() failed\n");
printf("Second bind()\n");
memset(&a, 0, sizeof(a));
a.sun_family = AF_UNIX;
strncpy(a.sun_path, "/tmp/.first_bind_failed", sizeof(a.sun_path));
if ((bind(s, (const struct sockaddr*) &a, sizeof(a))) == -1)
perror("bind() failed\n");
}
kda@SLES15-SP2:~> ./test
First bind()
Second bind()
bind() failed
: Invalid argument
kda@SLES15-SP2:~> ls -la /tmp/.first_bind
.first_bind .first_bind_failed
Signed-off-by: Denis Kirjanov <kda@linux-powerpc.org>
---
net/unix/af_unix.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 41c3303c3357..fd76a8fe3907 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1021,7 +1021,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
err = -EINVAL;
if (addr_len < offsetofend(struct sockaddr_un, sun_family) ||
- sunaddr->sun_family != AF_UNIX)
+ sunaddr->sun_family != AF_UNIX || u->addr)
goto out;
if (addr_len == sizeof(short)) {
@@ -1049,10 +1049,6 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (err)
goto out_put;
- err = -EINVAL;
- if (u->addr)
- goto out_up;
-
err = -ENOMEM;
addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);
if (!addr)
--
2.16.4
^ permalink raw reply related
* [PATCH iproute2 1/1] tc flower: fix parsing vlan_id and vlan_prio
From: Roi Dayan @ 2020-11-24 12:26 UTC (permalink / raw)
To: netdev; +Cc: Roi Dayan, Simon Horman, David Ahern, zahari.doychev, jianbol,
jhs
When protocol is vlan then eth_type is set to the vlan eth type.
So when parsing vlan_id and vlan_prio need to check tc_proto
is vlan and not eth_type.
Fixes: 4c551369e083 ("tc flower: use right ethertype in icmp/arp parsing")
Signed-off-by: Roi Dayan <roid@nvidia.com>
---
tc/f_flower.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tc/f_flower.c b/tc/f_flower.c
index 58e1140d7391..9b278f3c0e83 100644
--- a/tc/f_flower.c
+++ b/tc/f_flower.c
@@ -1432,7 +1432,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
__u16 vid;
NEXT_ARG();
- if (!eth_type_vlan(eth_type)) {
+ if (!eth_type_vlan(tc_proto)) {
fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD\n");
return -1;
}
@@ -1446,7 +1446,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
__u8 vlan_prio;
NEXT_ARG();
- if (!eth_type_vlan(eth_type)) {
+ if (!eth_type_vlan(tc_proto)) {
fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD\n");
return -1;
}
--
2.25.4
^ permalink raw reply related
* [PATCH iproute2-next 1/1] tc flower: fix parsing vlan_id and vlan_prio
From: Roi Dayan @ 2020-11-24 12:28 UTC (permalink / raw)
To: netdev; +Cc: Roi Dayan, Simon Horman, David Ahern, zahari.doychev, jianbol,
jhs
When protocol is vlan then eth_type is set to the vlan eth type.
So when parsing vlan_id and vlan_prio need to check tc_proto
is vlan and not eth_type.
Fixes: 4c551369e083 ("tc flower: use right ethertype in icmp/arp parsing")
Signed-off-by: Roi Dayan <roid@nvidia.com>
---
tc/f_flower.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tc/f_flower.c b/tc/f_flower.c
index 58e1140d7391..9b278f3c0e83 100644
--- a/tc/f_flower.c
+++ b/tc/f_flower.c
@@ -1432,7 +1432,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
__u16 vid;
NEXT_ARG();
- if (!eth_type_vlan(eth_type)) {
+ if (!eth_type_vlan(tc_proto)) {
fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD\n");
return -1;
}
@@ -1446,7 +1446,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
__u8 vlan_prio;
NEXT_ARG();
- if (!eth_type_vlan(eth_type)) {
+ if (!eth_type_vlan(tc_proto)) {
fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD\n");
return -1;
}
--
2.25.4
^ permalink raw reply related
* Re: [PATCH iproute2 1/1] tc flower: fix parsing vlan_id and vlan_prio
From: Roi Dayan @ 2020-11-24 12:28 UTC (permalink / raw)
To: netdev; +Cc: Simon Horman, David Ahern, zahari.doychev, jianbol, jhs
In-Reply-To: <20201124122641.46696-1-roid@nvidia.com>
On 2020-11-24 2:26 PM, Roi Dayan wrote:
> When protocol is vlan then eth_type is set to the vlan eth type.
> So when parsing vlan_id and vlan_prio need to check tc_proto
> is vlan and not eth_type.
>
> Fixes: 4c551369e083 ("tc flower: use right ethertype in icmp/arp parsing")
> Signed-off-by: Roi Dayan <roid@nvidia.com>
> ---
> tc/f_flower.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/tc/f_flower.c b/tc/f_flower.c
> index 58e1140d7391..9b278f3c0e83 100644
> --- a/tc/f_flower.c
> +++ b/tc/f_flower.c
> @@ -1432,7 +1432,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
> __u16 vid;
>
> NEXT_ARG();
> - if (!eth_type_vlan(eth_type)) {
> + if (!eth_type_vlan(tc_proto)) {
> fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD\n");
> return -1;
> }
> @@ -1446,7 +1446,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
> __u8 vlan_prio;
>
> NEXT_ARG();
> - if (!eth_type_vlan(eth_type)) {
> + if (!eth_type_vlan(tc_proto)) {
> fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD\n");
> return -1;
> }
>
sorry should have tagged as iproute2-next.
ignore this i sent with correct tag.
^ permalink raw reply
* Re: [PATCH net-next v5 3/5] net/lapb: fix t1 timer handling for LAPB_STATE_0
From: Martin Schiller @ 2020-11-24 12:29 UTC (permalink / raw)
To: David Laight
Cc: andrew.hendry, davem, kuba, xie.he.0141, linux-x25, netdev,
linux-kernel
In-Reply-To: <2d40b42aee314611b9ba1627e5eab30b@AcuMS.aculab.com>
On 2020-11-24 12:43, David Laight wrote:
> From: Martin Schiller
>> Sent: 24 November 2020 09:36
>>
>> 1. DTE interface changes immediately to LAPB_STATE_1 and start sending
>> SABM(E).
>>
>> 2. DCE interface sends N2-times DM and changes to LAPB_STATE_1
>> afterwards if there is no response in the meantime.
>
> Seems reasonable.
> It is 35 years since I wrote LAPB and I can't exactly remember
> what we did.
> If I stole a copy of the code it's on a QIC-150 tape cartridge!
>
> I really don't remember having a DTE/DCE option.
> It is likely that LAPB came up sending DM (response without F)
> until level3 requested the link come up when it would send
> N2 SABM+P hoping to get a UA+F.
> It would then send DM-F until a retry request was made.
>
> We certainly had several different types of crossover connectors
> for DTE-DTE working.
>
> David
>
The support for DTE/DCE was already in the LAPB code and I made it
configurable from userspace (at least for hdlc interfaces) with this
commit:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=f362e5fe0f1f
For Layer3 (X.25) I will add it with an addional patch (you already
commented on that) on a next step.
The described behaviour above is my interpretation of point 2.4.4.1 of
the "ITU-T Recommendation X.25 (10/96) aka "Blue Book" [1].
[1] https://www.itu.int/rec/T-REC-X.25-199610-I/
>>
>> Signed-off-by: Martin Schiller <ms@dev.tdt.de>
>> ---
>> net/lapb/lapb_timer.c | 11 +++++++++--
>> 1 file changed, 9 insertions(+), 2 deletions(-)
>>
>> diff --git a/net/lapb/lapb_timer.c b/net/lapb/lapb_timer.c
>> index 8f5b17001a07..baa247fe4ed0 100644
>> --- a/net/lapb/lapb_timer.c
>> +++ b/net/lapb/lapb_timer.c
>> @@ -85,11 +85,18 @@ static void lapb_t1timer_expiry(struct timer_list
>> *t)
>> switch (lapb->state) {
>>
>> /*
>> - * If we are a DCE, keep going DM .. DM .. DM
>> + * If we are a DCE, send DM up to N2 times, then switch to
>> + * STATE_1 and send SABM(E).
>> */
>> case LAPB_STATE_0:
>> - if (lapb->mode & LAPB_DCE)
>> + if (lapb->mode & LAPB_DCE &&
>> + lapb->n2count != lapb->n2) {
>> + lapb->n2count++;
>> lapb_send_control(lapb, LAPB_DM, LAPB_POLLOFF, LAPB_RESPONSE);
>> + } else {
>> + lapb->state = LAPB_STATE_1;
>> + lapb_establish_data_link(lapb);
>> + }
>> break;
>>
>> /*
>> --
>> 2.20.1
>
> -
> Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes,
> MK1 1PT, UK
> Registration No: 1397386 (Wales)
^ permalink raw reply
* [PATCH net v2] net: openvswitch: fix TTL decrement action netlink message format
From: Eelco Chaudron @ 2020-11-24 12:34 UTC (permalink / raw)
To: netdev; +Cc: davem, dev, kuba, pshelar, bindiyakurle, i.maximets, mcroce
Currently, the openvswitch module is not accepting the correctly formated
netlink message for the TTL decrement action. For both setting and getting
the dec_ttl action, the actions should be nested in the
OVS_DEC_TTL_ATTR_ACTION attribute as mentioned in the openvswitch.h uapi.
When the original patch was sent, it was tested with a private OVS userspace
implementation. This implementation was unfortunately not upstreamed and
reviewed, hence an erroneous version of this patch was sent out.
Leaving the patch as-is would cause problems as the kernel module could
interpret additional attributes as actions and vice-versa, due to the
actions not being encapsulated/nested within the actual attribute, but
being concatinated after it.
Fixes: 744676e77720 ("openvswitch: add TTL decrement action")
Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
---
v2: Update commit message to better explain the issue with the existing patch
include/uapi/linux/openvswitch.h | 2 +
net/openvswitch/actions.c | 7 ++--
net/openvswitch/flow_netlink.c | 74 ++++++++++++++++++++++++++++----------
3 files changed, 60 insertions(+), 23 deletions(-)
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index 8300cc29dec8..8d16744edc31 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -1058,4 +1058,6 @@ enum ovs_dec_ttl_attr {
__OVS_DEC_TTL_ATTR_MAX
};
+#define OVS_DEC_TTL_ATTR_MAX (__OVS_DEC_TTL_ATTR_MAX - 1)
+
#endif /* _LINUX_OPENVSWITCH_H */
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index b87bfc82f44f..5829a020b81c 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -958,14 +958,13 @@ static int dec_ttl_exception_handler(struct datapath *dp, struct sk_buff *skb,
{
/* The first action is always 'OVS_DEC_TTL_ATTR_ARG'. */
struct nlattr *dec_ttl_arg = nla_data(attr);
- int rem = nla_len(attr);
if (nla_len(dec_ttl_arg)) {
- struct nlattr *actions = nla_next(dec_ttl_arg, &rem);
+ struct nlattr *actions = nla_data(dec_ttl_arg);
if (actions)
- return clone_execute(dp, skb, key, 0, actions, rem,
- last, false);
+ return clone_execute(dp, skb, key, 0, nla_data(actions),
+ nla_len(actions), last, false);
}
consume_skb(skb);
return 0;
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 9d3e50c4d29f..ec0689ddc635 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -2503,28 +2503,42 @@ static int validate_and_copy_dec_ttl(struct net *net,
__be16 eth_type, __be16 vlan_tci,
u32 mpls_label_count, bool log)
{
- int start, err;
- u32 nested = true;
+ const struct nlattr *attrs[OVS_DEC_TTL_ATTR_MAX + 1];
+ int start, action_start, err, rem;
+ const struct nlattr *a, *actions;
+
+ memset(attrs, 0, sizeof(attrs));
+ nla_for_each_nested(a, attr, rem) {
+ int type = nla_type(a);
- if (!nla_len(attr))
- return ovs_nla_add_action(sfa, OVS_ACTION_ATTR_DEC_TTL,
- NULL, 0, log);
+ /* Ignore unknown attributes to be future proof. */
+ if (type > OVS_DEC_TTL_ATTR_MAX)
+ continue;
+
+ if (!type || attrs[type])
+ return -EINVAL;
+
+ attrs[type] = a;
+ }
+
+ actions = attrs[OVS_DEC_TTL_ATTR_ACTION];
+ if (rem || !actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN))
+ return -EINVAL;
start = add_nested_action_start(sfa, OVS_ACTION_ATTR_DEC_TTL, log);
if (start < 0)
return start;
- err = ovs_nla_add_action(sfa, OVS_DEC_TTL_ATTR_ACTION, &nested,
- sizeof(nested), log);
-
- if (err)
- return err;
+ action_start = add_nested_action_start(sfa, OVS_DEC_TTL_ATTR_ACTION, log);
+ if (action_start < 0)
+ return start;
- err = __ovs_nla_copy_actions(net, attr, key, sfa, eth_type,
+ err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type,
vlan_tci, mpls_label_count, log);
if (err)
return err;
+ add_nested_action_end(*sfa, action_start);
add_nested_action_end(*sfa, start);
return 0;
}
@@ -3487,20 +3501,42 @@ static int check_pkt_len_action_to_attr(const struct nlattr *attr,
static int dec_ttl_action_to_attr(const struct nlattr *attr,
struct sk_buff *skb)
{
- int err = 0, rem = nla_len(attr);
- struct nlattr *start;
+ struct nlattr *start, *action_start;
+ const struct nlattr *a;
+ int err = 0, rem;
start = nla_nest_start_noflag(skb, OVS_ACTION_ATTR_DEC_TTL);
-
if (!start)
return -EMSGSIZE;
- err = ovs_nla_put_actions(nla_data(attr), rem, skb);
- if (err)
- nla_nest_cancel(skb, start);
- else
- nla_nest_end(skb, start);
+ nla_for_each_attr(a, nla_data(attr), nla_len(attr), rem) {
+ switch (nla_type(a)) {
+ case OVS_DEC_TTL_ATTR_ACTION:
+
+ action_start = nla_nest_start_noflag(skb, OVS_DEC_TTL_ATTR_ACTION);
+ if (!action_start) {
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ err = ovs_nla_put_actions(nla_data(a), nla_len(a), skb);
+ if (err)
+ goto out;
+
+ nla_nest_end(skb, action_start);
+ break;
+ default:
+ /* Ignore all other option to be future compatible */
+ break;
+ }
+ }
+
+ nla_nest_end(skb, start);
+ return 0;
+
+out:
+ nla_nest_cancel(skb, start);
return err;
}
^ permalink raw reply related
* Re: [PATCH iproute2-next 1/1] tc flower: fix parsing vlan_id and vlan_prio
From: Zahari Doychev @ 2020-11-24 12:42 UTC (permalink / raw)
To: Roi Dayan; +Cc: netdev, Simon Horman, David Ahern, jianbol, jhs
In-Reply-To: <20201124122810.46790-1-roid@nvidia.com>
On Tue, Nov 24, 2020 at 02:28:10PM +0200, Roi Dayan wrote:
> When protocol is vlan then eth_type is set to the vlan eth type.
> So when parsing vlan_id and vlan_prio need to check tc_proto
> is vlan and not eth_type.
>
> Fixes: 4c551369e083 ("tc flower: use right ethertype in icmp/arp parsing")
> Signed-off-by: Roi Dayan <roid@nvidia.com>
> ---
> tc/f_flower.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/tc/f_flower.c b/tc/f_flower.c
> index 58e1140d7391..9b278f3c0e83 100644
> --- a/tc/f_flower.c
> +++ b/tc/f_flower.c
> @@ -1432,7 +1432,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
> __u16 vid;
>
> NEXT_ARG();
> - if (!eth_type_vlan(eth_type)) {
> + if (!eth_type_vlan(tc_proto)) {
> fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD\n");
> return -1;
> }
> @@ -1446,7 +1446,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
> __u8 vlan_prio;
>
> NEXT_ARG();
> - if (!eth_type_vlan(eth_type)) {
> + if (!eth_type_vlan(tc_proto)) {
> fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD\n");
> return -1;
> }
> --
Thanks for fixing this. I missed to test the option odering in my tests.
Your fix works fine for me. Sorry for missing this.
> 2.25.4
^ permalink raw reply
* RE: [PATCH net-next v3 4/7] dpaa_eth: add XDP_TX support
From: Camelia Alexandra Groza @ 2020-11-24 13:06 UTC (permalink / raw)
To: Maciej Fijalkowski
Cc: kuba@kernel.org, brouer@redhat.com, saeed@kernel.org,
davem@davemloft.net, Madalin Bucur (OSS), Ioana Ciornei,
netdev@vger.kernel.org
In-Reply-To: <20201123221829.GC11618@ranger.igk.intel.com>
> -----Original Message-----
> From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> Sent: Tuesday, November 24, 2020 00:18
> To: Camelia Alexandra Groza <camelia.groza@nxp.com>
> Cc: kuba@kernel.org; brouer@redhat.com; saeed@kernel.org;
> davem@davemloft.net; Madalin Bucur (OSS)
> <madalin.bucur@oss.nxp.com>; Ioana Ciornei <ioana.ciornei@nxp.com>;
> netdev@vger.kernel.org
> Subject: Re: [PATCH net-next v3 4/7] dpaa_eth: add XDP_TX support
>
> On Fri, Nov 20, 2020 at 06:54:42PM +0000, Camelia Alexandra Groza wrote:
> > > -----Original Message-----
> > > From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> > > Sent: Friday, November 20, 2020 01:51
> > > To: Camelia Alexandra Groza <camelia.groza@nxp.com>
> > > Cc: kuba@kernel.org; brouer@redhat.com; saeed@kernel.org;
> > > davem@davemloft.net; Madalin Bucur (OSS)
> > > <madalin.bucur@oss.nxp.com>; Ioana Ciornei <ioana.ciornei@nxp.com>;
> > > netdev@vger.kernel.org
> > > Subject: Re: [PATCH net-next v3 4/7] dpaa_eth: add XDP_TX support
> > >
> > > On Thu, Nov 19, 2020 at 06:29:33PM +0200, Camelia Groza wrote:
> > > > Use an xdp_frame structure for managing the frame. Store a
> backpointer
> > > to
> > > > the structure at the start of the buffer before enqueueing. Use the XDP
> > > > API for freeing the buffer when it returns to the driver on the TX
> > > > confirmation path.
> > >
>
> [...]
>
> > > > static u32 dpaa_run_xdp(struct dpaa_priv *priv, struct qm_fd *fd, void
> > > *vaddr,
> > > > - unsigned int *xdp_meta_len)
> > > > + struct dpaa_fq *dpaa_fq, unsigned int
> > > *xdp_meta_len)
> > > > {
> > > > ssize_t fd_off = qm_fd_get_offset(fd);
> > > > struct bpf_prog *xdp_prog;
> > > > + struct xdp_frame *xdpf;
> > > > struct xdp_buff xdp;
> > > > u32 xdp_act;
> > > >
> > > > @@ -2370,7 +2470,8 @@ static u32 dpaa_run_xdp(struct dpaa_priv
> *priv,
> > > struct qm_fd *fd, void *vaddr,
> > > > xdp.data_meta = xdp.data;
> > > > xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM;
> > > > xdp.data_end = xdp.data + qm_fd_get_length(fd);
> > > > - xdp.frame_sz = DPAA_BP_RAW_SIZE;
> > > > + xdp.frame_sz = DPAA_BP_RAW_SIZE - DPAA_TX_PRIV_DATA_SIZE;
> > > > + xdp.rxq = &dpaa_fq->xdp_rxq;
> > > >
> > > > xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp);
> > > >
> > > > @@ -2381,6 +2482,22 @@ static u32 dpaa_run_xdp(struct dpaa_priv
> *priv,
> > > struct qm_fd *fd, void *vaddr,
> > > > case XDP_PASS:
> > > > *xdp_meta_len = xdp.data - xdp.data_meta;
> > > > break;
> > > > + case XDP_TX:
> > > > + /* We can access the full headroom when sending the frame
> > > > + * back out
> > >
> > > And normally why a piece of headroom is taken away? I probably should
> > > have
> > > started from the basic XDP support patch, but if you don't mind, please
> > > explain it a bit.
> >
> > I mentioned we require DPAA_TX_PRIV_DATA_SIZE bytes at the start of
> the buffer in order to make sure we have enough space for our private info.
>
> What is your private info?
The dpaa_eth_swbp struct from the first patch. It's the xdp_frame reference mentioned in the patch description, stored for cleanup on confirmation. We also store a skb reference for non-XDP use cases.
> >
> > When setting up the xdp_buff, this area is reserved from the frame size
> exposed to the XDP program.
> > - xdp.frame_sz = DPAA_BP_RAW_SIZE;
> > + xdp.frame_sz = DPAA_BP_RAW_SIZE - DPAA_TX_PRIV_DATA_SIZE;
> >
> > After the XDP_TX verdict, we're sure that DPAA_TX_PRIV_DATA_SIZE
> bytes at the start of the buffer are free and we can use the full headroom
> how it suits us, hence the increase of the frame size back to
> DPAA_BP_RAW_SIZE.
>
> Not at the *end* of the buffer?
No, we store this information at the start of the buffer, before the XDP_PACKET_HEADROOM.
> >
> > Thanks for all your feedback.
> >
> > > > + */
> > > > + xdp.data_hard_start = vaddr;
> > > > + xdp.frame_sz = DPAA_BP_RAW_SIZE;
> > > > + xdpf = xdp_convert_buff_to_frame(&xdp);
> > > > + if (unlikely(!xdpf)) {
> > > > + free_pages((unsigned long)vaddr, 0);
> > > > + break;
> > > > + }
> > > > +
> > > > + if (dpaa_xdp_xmit_frame(priv->net_dev, xdpf))
> > > > + xdp_return_frame_rx_napi(xdpf);
> > > > +
> > > > + break;
> > > > default:
> > > > bpf_warn_invalid_xdp_action(xdp_act);
> > > > fallthrough;
> > > > @@ -2415,6 +2532,7 @@ static enum qman_cb_dqrr_result
^ permalink raw reply
* Re: [PATCH net-next] ip_gre: remove CRC flag from dev features in gre_gso_segment
From: Xin Long @ 2020-11-24 13:30 UTC (permalink / raw)
To: Alexander Duyck
Cc: network dev, linux-sctp @ vger . kernel . org,
Marcelo Ricardo Leitner, David Miller, Jakub Kicinski,
Guillaume Nault, Paolo Abeni, Lorenzo Bianconi
In-Reply-To: <CAKgT0Udkk9uEnjbPxrz7kxa=p-cysmkzqJX1Pw067dkbUceyHA@mail.gmail.com>
On Tue, Nov 24, 2020 at 6:23 AM Alexander Duyck
<alexander.duyck@gmail.com> wrote:
>
> On Mon, Nov 23, 2020 at 1:14 AM Xin Long <lucien.xin@gmail.com> wrote:
> >
> > On Sat, Nov 21, 2020 at 12:10 AM Alexander Duyck
> > <alexander.duyck@gmail.com> wrote:
> > >
> > > On Fri, Nov 20, 2020 at 2:23 AM Xin Long <lucien.xin@gmail.com> wrote:
> > > >
> > > > On Fri, Nov 20, 2020 at 1:24 AM Alexander Duyck
> > > > <alexander.duyck@gmail.com> wrote:
> > > > >
> > > > > On Wed, Nov 18, 2020 at 9:53 PM Xin Long <lucien.xin@gmail.com> wrote:
> > > > > >
> > > > > > On Thu, Nov 19, 2020 at 4:35 AM Alexander Duyck
> > > > > > <alexander.duyck@gmail.com> wrote:
> > > > > > >
> > > > > > > On Mon, Nov 16, 2020 at 1:17 AM Xin Long <lucien.xin@gmail.com> wrote:
> > > > > > > >
>
> <snip>
>
> > > > > > @@ -27,7 +27,11 @@ static __le32 sctp_gso_make_checksum(struct sk_buff *skb)
> > > > > > {
> > > > > > skb->ip_summed = CHECKSUM_NONE;
> > > > > > skb->csum_not_inet = 0;
> > > > > > - gso_reset_checksum(skb, ~0);
> > > > > > + /* csum and csum_start in GSO CB may be needed to do the UDP
> > > > > > + * checksum when it's a UDP tunneling packet.
> > > > > > + */
> > > > > > + SKB_GSO_CB(skb)->csum = (__force __wsum)~0;
> > > > > > + SKB_GSO_CB(skb)->csum_start = skb_headroom(skb) + skb->len;
> > > > > > return sctp_compute_cksum(skb, skb_transport_offset(skb));
> > > > > > }
> > > > > >
> > > > > > And yes, this patch has been tested with GRE tunnel checksums enabled.
> > > > > > (... icsum ocsum).
> > > > > > And yes, it was segmented in the lower NIC level, and we can make it by:
> > > > > >
> > > > > > # ethtool -K gre1 tx-sctp-segmentation on
> > > > > > # ethtool -K veth0 tx-sctp-segmentation off
> > > > > >
> > > > > > (Note: "tx-checksum-sctp" and "gso" are on for both devices)
> > > > > >
> > > > > > Thanks.
> > > > >
> > > > > I would also turn off Tx and Rx checksum offload on your veth device
> > > > > in order to make certain you aren't falsely sending data across
> > > > > indicating that the checksums are valid when they are not. It might be
> > > > > better if you were to run this over an actual NIC as that could then
> > > > > provide independent verification as it would be a fixed checksum test.
> > > > >
> > > > > I'm still not convinced this is working correctly. Basically a crc32c
> > > > > is not the same thing as a 1's complement checksum so you should need
> > > > > to compute both if you have an SCTP packet tunneled inside a UDP or
> > > > > GRE packet with a checksum. I don't see how computing a crc32c should
> > > > > automatically give you a 1's complement checksum of ~0.
> > > >
> > > > On the tx Path [1] below, the sctp crc checksum is calculated in
> > > > sctp_gso_make_checksum() [a], where it calls *sctp_compute_cksum()*
> > > > to do that, and as for the code in it:
> > > >
> > > > SKB_GSO_CB(skb)->csum = (__force __wsum)~0;
> > > > SKB_GSO_CB(skb)->csum_start = skb_headroom(skb) + skb->len;
> > >
> > > Okay, so I think I know how this is working, but the number of things
> > > relied on is ugly. Normally assuming skb_headroom(skb) + skb->len
> > > being valid for this would be a non-starter. I was assuming you
> > > weren't doing the 1's compliment checksum because you weren't using
> > > __skb_checksum to generate it.
> > >
> > > If I am not mistaken SCTP GSO uses the GSO_BY_FRAGS and apparently
> > > none of the frags are using page fragments within the skb. Am I
> > > understanding correctly? One thing that would help to address some of
> > > my concerns would be to clear instead of set NETIF_F_SG in
> > > sctp_gso_segment since your checksum depends on linear skbs.
> > Right, no frag is using page fragments for SCTP GSO.
> > NETIF_F_SG is set here, because in skb_segment():
> >
> > if (hsize > len || !sg)
> > hsize = len;
> >
> > if (!hsize && i >= nfrags && skb_headlen(list_skb) &&
> > (skb_headlen(list_skb) == len || sg)) { <------
> > for flag_list
> >
> > without sg set, it won't go to this 'if' block, which is the process
> > of frag_list, see
>
> I don't think that is processing frag_list, it is processing frags. It
> is just updating list_skb as needed, however it is also configured
> outside of that block.
For SCTP's gso, we expect it going to the branch of matching
(skb_headlen(list_skb) == len), as it will reuse the skb->data.
>
> > commit 89319d3801d1d3ac29c7df1f067038986f267d29
> > Author: Herbert Xu <herbert@gondor.apana.org.au>
> > Date: Mon Dec 15 23:26:06 2008 -0800
> >
> > net: Add frag_list support to skb_segment
> >
> > do you think this might be a bug in skb_segment()?
>
> I would say it is assuming your logic is correct. Basically it should
> be able to segment the frame regardless of if the lower device
> supports NETIF_F_SG or not.
>
> > I was also thinking if SCTP GSO could go with the way of UDP's
> > with skb_segment_list() instead of GSO_BY_FRAGS things.
> > the different is that the head skb does not only include header,
> > but may also include userdata/payload with skb_segment_list().
>
> The problem is right now the way the checksum is being configured you
> would have to keep the payload and data all in one logical data
> segment starting at skb->data. We cannot have any data stored in
> shinfo->frags, nor shinfo->frag_list.
That's right, current SCTP protocol stack don't save tx data into
frags or frag_list, and SCTP doesn't support GRO by now.
>
> > >
> > > > is prepared for doing 1's complement checksum (for outer UDP/GRE), and used
> > > > in gre_gso_segment() [b], where it calls gso_make_checksum() to do that
> > > > when need_csum is set. Note that, here it played a TRICK:
> > > >
> > > > I set SKB_GSO_CB->csum_start to the end of this packet and
> > > > SKB_GSO_CB->csum = ~0 manually, so that in gso_make_checksum() it will do
> > > > the 1's complement checksum for outer UDP/GRE by summing all packet bits up.
> > > > See gso_make_checksum() (called by gre_gso_segment()):
> > > >
> > > > unsigned char *csum_start = skb_transport_header(skb);
> > > > int plen = (skb->head + SKB_GSO_CB(skb)->csum_start) - csum_start;
> > > > /* now plen is from udp header to the END of packet. */
> > > > __wsum partial = SKB_GSO_CB(skb)->csum;
> > > >
> > > > return csum_fold(csum_partial(csum_start, plen, partial));
> > > >
> > > > So yes, here it does compute both if I have an SCTP packet tunnelled inside
> > > > a UDP or GRE packet with a checksum.
> > >
> > > Assuming you have the payload data in the skb->data section. Normally
> > > payload is in page frags. That is why I was concerned. You have to
> > > have guarantees in place that there will not be page fragments
> > > attached to the skb.
> > On SCTP TX path, sctp_packet_transmit() will always alloc linear skbs
> > and reserve headers for lower-layer protocols. I think this will guarantee it.
>
> That ends up being my big concern. We need to make certain that is
> true for all GRO and GSO cases if we are going to operate on the
> assumption that just doing a linear checksum will work in the GSO
> code. Otherwise we need to make certain that segmentation will correct
> this for us if it cannot be guaranteed. That is why I would be much
> more comfortable if we were able to just drop the NETIF_F_SG bit when
> doing the segmentation since that would guarantee the results we are
> looking for.
before doing that, we should have a fix below:
@@ -3850,8 +3850,6 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
hsize = skb_headlen(head_skb) - offset;
if (hsize < 0)
hsize = 0;
- if (hsize > len || !sg)
- hsize = len;
if (!hsize && i >= nfrags && skb_headlen(list_skb) &&
(skb_headlen(list_skb) == len || sg)) {
@@ -3896,6 +3894,9 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
skb_release_head_state(nskb);
__skb_push(nskb, doffset);
} else {
+ if (hsize > len || !sg)
+ hsize = len;
+
I believe it makes more sense to move this check to the 'else' branch.
^ permalink raw reply
* Re: [PATCH net-next] net: sched: alias action flags with TCA_ACT_ prefix
From: Jamal Hadi Salim @ 2020-11-24 13:39 UTC (permalink / raw)
To: Vlad Buslov, Jakub Kicinski; +Cc: netdev, davem, xiyou.wangcong, jiri
In-Reply-To: <87v9dv9k8q.fsf@buslov.dev>
On 2020-11-24 4:28 a.m., Vlad Buslov wrote:
> On Mon 23 Nov 2020 at 23:22, Jakub Kicinski <kuba@kernel.org> wrote:
>> On Sat, 21 Nov 2020 18:09:02 +0200 Vlad Buslov wrote:
>>> Currently both filter and action flags use same "TCA_" prefix which makes
>>> them hard to distinguish to code and confusing for users. Create aliases
>>> for existing action flags constants with "TCA_ACT_" prefix.
>>>
>>> Signed-off-by: Vlad Buslov <vlad@buslov.dev>
>>
>> Are we expecting to add both aliases for all new flags?
>
> I don't think it makes sense to have both aliases for any new flags.
>
Agreed.
cheers,
jamal
^ permalink raw reply
* Re: [PATCH net-next v2 0/3] net: ptp: use common defines for PTP message types in further drivers
From: Richard Cochran @ 2020-11-24 13:39 UTC (permalink / raw)
To: Christian Eggers
Cc: Andrew Lunn, Heiner Kallweit, Jakub Kicinski, Vladimir Oltean,
Russell King, David S . Miller, netdev, linux-kernel
In-Reply-To: <20201124074418.2609-1-ceggers@arri.de>
On Tue, Nov 24, 2020 at 08:44:15AM +0100, Christian Eggers wrote:
> Changes in v2:
> ----------------
> - resend, as v1 was sent before the prerequisites were merged
> - removed mismatch between From: and Signed-off-by:
> - [2/3] Reviewed-by: Ido Schimmel <idosch@nvidia.com>
> - [3/3] Reviewed-by: Antoine Tenart <atenart@kernel.org>
> - [3/3] removed dead email addresses from Cc:
>
>
> This series replaces further driver internal enumeration / uses of magic
> numbers with the newly introduced PTP_MSGTYPE_* defines.
For the series:
Acked-by: Richard Cochran <richardcochran@gmail.com>
^ permalink raw reply
* Re: [PATCH iproute2-next v2] tc flower: use right ethertype in icmp/arp parsing
From: Jamal Hadi Salim @ 2020-11-24 13:41 UTC (permalink / raw)
To: Roi Dayan, David Ahern, Zahari Doychev, netdev; +Cc: simon.horman, jianbol
In-Reply-To: <6bf5c24a-13bf-afbd-0b45-1488c09ecc56@nvidia.com>
On 2020-11-24 7:13 a.m., Roi Dayan wrote:
>
>
> On 2020-11-24 11:39 AM, Roi Dayan wrote:
>>
>>
[..]
>> Hi,
>>
>> I didn't debug yet but with this commit I am failing to add a tc
>> rule I always could before. also the error msg doesn't make sense.
>>
>> Example:
>>
>> # tc filter add dev enp8s0f0 protocol 802.1Q parent ffff: prio 1 flower\
>> skip_hw dst_mac e4:11:22:11:4a:51 src_mac e4:11:22:11:4a:50\
>> vlan_ethtype 0x800 vlan_id 100 vlan_prio 0 action vlan pop action\
>> mirred egress redirect dev enp8s0f0_0
>>
>>
>> Can't set "vlan_id" if ethertype isn't 802.1Q or 802.1AD
>>
>>
>> I used protocol 802.1Q and vlan_ethtype 0x800.
>> am i missing something? the rule should look different now?
>>
>> Thanks,
>> Roi
>
>
> Hi,
>
> I debugged this and it break vlan rules.
> The issue is from this part of the change
>
We have a test for this in tdc. Maybe we should make it a rule to
run tdc tests on changes like these?
cheers,
jamal
^ permalink raw reply
* RE: [PATCH net-next v3 2/7] dpaa_eth: add basic XDP support
From: Camelia Alexandra Groza @ 2020-11-24 13:47 UTC (permalink / raw)
To: Maciej Fijalkowski
Cc: kuba@kernel.org, brouer@redhat.com, saeed@kernel.org,
davem@davemloft.net, Madalin Bucur (OSS), Ioana Ciornei,
netdev@vger.kernel.org
In-Reply-To: <20201123220752.GB11618@ranger.igk.intel.com>
> -----Original Message-----
> From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> Sent: Tuesday, November 24, 2020 00:08
> To: Camelia Alexandra Groza <camelia.groza@nxp.com>
> Cc: kuba@kernel.org; brouer@redhat.com; saeed@kernel.org;
> davem@davemloft.net; Madalin Bucur (OSS)
> <madalin.bucur@oss.nxp.com>; Ioana Ciornei <ioana.ciornei@nxp.com>;
> netdev@vger.kernel.org
> Subject: Re: [PATCH net-next v3 2/7] dpaa_eth: add basic XDP support
>
> On Fri, Nov 20, 2020 at 06:50:28PM +0000, Camelia Alexandra Groza wrote:
> > > -----Original Message-----
> > > From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> > > Sent: Friday, November 20, 2020 02:19
> > > To: Camelia Alexandra Groza <camelia.groza@nxp.com>
> > > Cc: kuba@kernel.org; brouer@redhat.com; saeed@kernel.org;
> > > davem@davemloft.net; Madalin Bucur (OSS)
> > > <madalin.bucur@oss.nxp.com>; Ioana Ciornei <ioana.ciornei@nxp.com>;
> > > netdev@vger.kernel.org
> > > Subject: Re: [PATCH net-next v3 2/7] dpaa_eth: add basic XDP support
> > >
> > > On Thu, Nov 19, 2020 at 06:29:31PM +0200, Camelia Groza wrote:
> > > > Implement the XDP_DROP and XDP_PASS actions.
> > > >
> > > > Avoid draining and reconfiguring the buffer pool at each XDP
> > > > setup/teardown by increasing the frame headroom and reserving
> > > > XDP_PACKET_HEADROOM bytes from the start. Since we always
> reserve
> > > an
> > > > entire page per buffer, this change only impacts Jumbo frame scenarios
> > > > where the maximum linear frame size is reduced by 256 bytes. Multi
> > > > buffer Scatter/Gather frames are now used instead in these scenarios.
> > > >
> > > > Allow XDP programs to access the entire buffer.
> > > >
> > > > The data in the received frame's headroom can be overwritten by the
> XDP
> > > > program. Extract the relevant fields from the headroom while they are
> > > > still available, before running the XDP program.
> > > >
> > > > Since the headroom might be resized before the frame is passed up to
> the
> > > > stack, remove the check for a fixed headroom value when building an
> skb.
> > > >
> > > > Allow the meta data to be updated and pass the information up the
> stack.
> > > >
> > > > Scatter/Gather frames are dropped when XDP is enabled.
> > > >
> > > > Acked-by: Madalin Bucur <madalin.bucur@oss.nxp.com>
> > > > Signed-off-by: Camelia Groza <camelia.groza@nxp.com>
> > > > ---
> > > > Changes in v2:
> > > > - warn only once if extracting the timestamp from a received frame fails
> > > >
> > > > Changes in v3:
> > > > - drop received S/G frames when XDP is enabled
> > > >
> > > > drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 158
> > > ++++++++++++++++++++++---
> > > > drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 2 +
> > > > 2 files changed, 144 insertions(+), 16 deletions(-)
> > > >
> > > > diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> > > b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> > > > index 88533a2..102023c 100644
> > > > --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> > > > +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> > > > @@ -53,6 +53,8 @@
> > > > #include <linux/dma-mapping.h>
> > > > #include <linux/sort.h>
> > > > #include <linux/phy_fixed.h>
> > > > +#include <linux/bpf.h>
> > > > +#include <linux/bpf_trace.h>
> > > > #include <soc/fsl/bman.h>
> > > > #include <soc/fsl/qman.h>
> > > > #include "fman.h"
> > > > @@ -177,7 +179,7 @@
> > > > #define DPAA_HWA_SIZE (DPAA_PARSE_RESULTS_SIZE +
> > > DPAA_TIME_STAMP_SIZE \
> > > > + DPAA_HASH_RESULTS_SIZE)
> > > > #define DPAA_RX_PRIV_DATA_DEFAULT_SIZE
> > > (DPAA_TX_PRIV_DATA_SIZE + \
> > > > - dpaa_rx_extra_headroom)
> > > > + XDP_PACKET_HEADROOM -
> > > DPAA_HWA_SIZE)
> > > > #ifdef CONFIG_DPAA_ERRATUM_A050385
> > > > #define DPAA_RX_PRIV_DATA_A050385_SIZE (DPAA_A050385_ALIGN
> -
> > > DPAA_HWA_SIZE)
> > > > #define DPAA_RX_PRIV_DATA_SIZE (fman_has_errata_a050385() ? \
> > > > @@ -1733,7 +1735,6 @@ static struct sk_buff *contig_fd_to_skb(const
> > > struct dpaa_priv *priv,
> > > > SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
> > > > if (WARN_ONCE(!skb, "Build skb failure on Rx\n"))
> > > > goto free_buffer;
> > > > - WARN_ON(fd_off != priv->rx_headroom);
> > > > skb_reserve(skb, fd_off);
> > > > skb_put(skb, qm_fd_get_length(fd));
> > > >
> > > > @@ -2349,12 +2350,62 @@ static enum qman_cb_dqrr_result
> > > rx_error_dqrr(struct qman_portal *portal,
> > > > return qman_cb_dqrr_consume;
> > > > }
> > > >
> > > > +static u32 dpaa_run_xdp(struct dpaa_priv *priv, struct qm_fd *fd,
> void
> > > *vaddr,
> > > > + unsigned int *xdp_meta_len)
> > > > +{
> > > > + ssize_t fd_off = qm_fd_get_offset(fd);
> > > > + struct bpf_prog *xdp_prog;
> > > > + struct xdp_buff xdp;
> > > > + u32 xdp_act;
> > > > +
> > > > + rcu_read_lock();
> > > > +
> > > > + xdp_prog = READ_ONCE(priv->xdp_prog);
> > > > + if (!xdp_prog) {
> > > > + rcu_read_unlock();
> > > > + return XDP_PASS;
> > > > + }
> > > > +
> > > > + xdp.data = vaddr + fd_off;
> > >
> > > I feel like a little drawing of xdp_buff layout would help me with
> > > understanding what is going on over here :)
> >
> > A region at the start of the buffer is reserved for storing hardware
> annotations and room for the XDP_PACKET_HEADROOM, before the actual
> data starts. So vaddr points to the start of the buffer, while fd offset provides
> the offset of the data inside the buffer. I don't feel that we are filling the
> xdp_buff in a majorly different way from other drivers, so please mention
> what is unclear here and I can provide more details.
>
> Okay, so fd_off tells me where the frame starts, from vaddr to vaddr +
> fd_off there might be some HW provided data, so you extract it and then
> you are free to go with setting the data_hard_start?
Yes, that's right.
> >
> > > > + xdp.data_meta = xdp.data;
> > > > + xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM;
> > > > + xdp.data_end = xdp.data + qm_fd_get_length(fd);
> > > > + xdp.frame_sz = DPAA_BP_RAW_SIZE;
> > >
> > > Maybe you could fill xdp_buff outside of this function so that later on
> > > you could set xdp.rxq once per napi?
> >
> > I admit I haven't looked into exactly how much performance we would gain
> from this, but I don't think it would be enough to justify the code churn. We
> don't have a clean loop for processing the received frames like I see the Intel
> and ENA drivers have.
> >
> > > > +
> > > > + xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp);
> > > > +
> > > > + /* Update the length and the offset of the FD */
> > > > + qm_fd_set_contig(fd, xdp.data - vaddr, xdp.data_end - xdp.data);
> > > > +
> > > > + switch (xdp_act) {
> > > > + case XDP_PASS:
> > > > + *xdp_meta_len = xdp.data - xdp.data_meta;
> > > > + break;
> > > > + default:
> > > > + bpf_warn_invalid_xdp_action(xdp_act);
> > > > + fallthrough;
> > > > + case XDP_ABORTED:
> > > > + trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act);
> > > > + fallthrough;
> > > > + case XDP_DROP:
> > > > + /* Free the buffer */
> > > > + free_pages((unsigned long)vaddr, 0);
> > > > + break;
> > > > + }
> > > > +
> > > > + rcu_read_unlock();
> > > > +
> > > > + return xdp_act;
> > > > +}
> > > > +
> > > > static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal
> > > *portal,
> > > > struct qman_fq *fq,
> > > > const struct qm_dqrr_entry
> > > *dq,
> > > > bool sched_napi)
> > > > {
> > > > + bool ts_valid = false, hash_valid = false;
> > > > struct skb_shared_hwtstamps *shhwtstamps;
> > > > + unsigned int skb_len, xdp_meta_len = 0;
> > > > struct rtnl_link_stats64 *percpu_stats;
> > > > struct dpaa_percpu_priv *percpu_priv;
> > > > const struct qm_fd *fd = &dq->fd;
> > > > @@ -2362,12 +2413,14 @@ static enum qman_cb_dqrr_result
> > > rx_default_dqrr(struct qman_portal *portal,
> > > > enum qm_fd_format fd_format;
> > > > struct net_device *net_dev;
> > > > u32 fd_status, hash_offset;
> > > > + struct qm_sg_entry *sgt;
> > > > struct dpaa_bp *dpaa_bp;
> > > > struct dpaa_priv *priv;
> > > > - unsigned int skb_len;
> > > > struct sk_buff *skb;
> > > > int *count_ptr;
> > > > + u32 xdp_act;
> > > > void *vaddr;
> > > > + u32 hash;
> > > > u64 ns;
> > > >
> > > > fd_status = be32_to_cpu(fd->status);
> > > > @@ -2423,35 +2476,67 @@ static enum qman_cb_dqrr_result
> > > rx_default_dqrr(struct qman_portal *portal,
> > > > count_ptr = this_cpu_ptr(dpaa_bp->percpu_count);
> > > > (*count_ptr)--;
> > > >
> > > > - if (likely(fd_format == qm_fd_contig))
> > > > + /* Extract the timestamp stored in the headroom before running
> > > XDP */
> > > > + if (priv->rx_tstamp) {
> > > > + if (!fman_port_get_tstamp(priv->mac_dev->port[RX], vaddr,
> > > &ns))
> > > > + ts_valid = true;
> > > > + else
> > > > + WARN_ONCE(1, "fman_port_get_tstamp failed!\n");
> > > > + }
> > > > +
> > > > + /* Extract the hash stored in the headroom before running XDP */
> > > > + if (net_dev->features & NETIF_F_RXHASH && priv->keygen_in_use
> > > &&
> > > > + !fman_port_get_hash_result_offset(priv->mac_dev->port[RX],
> > > > + &hash_offset)) {
> > > > + hash = be32_to_cpu(*(u32 *)(vaddr + hash_offset));
> > > > + hash_valid = true;
> > > > + }
> > > > +
> > > > + if (likely(fd_format == qm_fd_contig)) {
> > > > + xdp_act = dpaa_run_xdp(priv, (struct qm_fd *)fd, vaddr,
> > > > + &xdp_meta_len);
> > > > + if (xdp_act != XDP_PASS) {
> > > > + percpu_stats->rx_packets++;
> > > > + percpu_stats->rx_bytes += qm_fd_get_length(fd);
> > > > + return qman_cb_dqrr_consume;
> > > > + }
> > > > skb = contig_fd_to_skb(priv, fd);
> > > > - else
> > > > + } else {
> > > > + /* XDP doesn't support S/G frames. Return the fragments to
> > > the
> > > > + * buffer pool and release the SGT.
> > > > + */
> > > > + if (READ_ONCE(priv->xdp_prog)) {
> > > > + WARN_ONCE(1, "S/G frames not supported under
> > > XDP\n");
> > > > + sgt = vaddr + qm_fd_get_offset(fd);
> > > > + dpaa_release_sgt_members(sgt);
> > > > + free_pages((unsigned long)vaddr, 0);
> > > > + return qman_cb_dqrr_consume;
> > > > + }
> > > > skb = sg_fd_to_skb(priv, fd);
> > > > + }
> > > > if (!skb)
> > > > return qman_cb_dqrr_consume;
> > > >
> > > > - if (priv->rx_tstamp) {
> > > > + if (xdp_meta_len)
> > > > + skb_metadata_set(skb, xdp_meta_len);
> > >
> > > This is working on a single buffer, right? So there's no need to clear
> > > xdp_meta_len?
> >
> > I don't think I understand what you mean. Are you saying I shouldn't be
> initializing xdp_meta_len to 0? This receive path is used when XDP is disabled
> as well, in which case we don't propagate the metadata.
>
> What I meant was that if this function would operate on many buffers then
> we would have to clear the xdp_meta_len, so that next buffers wouldn't get
> the value from previous bufs, but I suppose that this rx_default_dqrr
> callback is called once per each buffer.
Yes, rx_default_dqrr is processing only one buffer.
> >
> > > > +
> > > > + /* Set the previously extracted timestamp */
> > > > + if (ts_valid) {
> > > > shhwtstamps = skb_hwtstamps(skb);
> > > > memset(shhwtstamps, 0, sizeof(*shhwtstamps));
> > > > -
> > > > - if (!fman_port_get_tstamp(priv->mac_dev->port[RX], vaddr,
> > > &ns))
> > > > - shhwtstamps->hwtstamp = ns_to_ktime(ns);
> > > > - else
> > > > - dev_warn(net_dev->dev.parent,
> > > "fman_port_get_tstamp failed!\n");
> > > > + shhwtstamps->hwtstamp = ns_to_ktime(ns);
> > > > }
> > > >
> > > > skb->protocol = eth_type_trans(skb, net_dev);
> > > >
> > > > - if (net_dev->features & NETIF_F_RXHASH && priv->keygen_in_use
> > > &&
> > > > - !fman_port_get_hash_result_offset(priv->mac_dev->port[RX],
> > > > - &hash_offset)) {
> > > > + /* Set the previously extracted hash */
> > > > + if (hash_valid) {
> > > > enum pkt_hash_types type;
> > > >
> > > > /* if L4 exists, it was used in the hash generation */
> > > > type = be32_to_cpu(fd->status) & FM_FD_STAT_L4CV ?
> > > > PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3;
> > > > - skb_set_hash(skb, be32_to_cpu(*(u32 *)(vaddr +
> > > hash_offset)),
> > > > - type);
> > > > + skb_set_hash(skb, hash, type);
> > > > }
> > > >
> > > > skb_len = skb->len;
> > > > @@ -2671,6 +2756,46 @@ static int dpaa_eth_stop(struct net_device
> > > *net_dev)
> > > > return err;
> > > > }
^ permalink raw reply
* [PATCH 0/1] pull request for net: batman-adv 2020-11-24
From: Simon Wunderlich @ 2020-11-24 13:44 UTC (permalink / raw)
To: davem, kuba; +Cc: netdev, b.a.t.m.a.n, Simon Wunderlich
Hi David, hi Jakub,
here is a bugfix for batman-adv which we would like to have integrated into net.
Please pull or let me know of any problem!
Thank you,
Simon
The following changes since commit f8394f232b1eab649ce2df5c5f15b0e528c92091:
Linux 5.10-rc3 (2020-11-08 16:10:16 -0800)
are available in the Git repository at:
git://git.open-mesh.org/linux-merge.git tags/batadv-net-pullrequest-20201124
for you to fetch changes up to 14a2e551faea53d45bc11629a9dac88f88950ca7:
batman-adv: set .owner to THIS_MODULE (2020-11-15 11:43:56 +0100)
----------------------------------------------------------------
Here is a batman-adv bugfix:
- set module owner to THIS_MODULE, by Taehee Yoo
----------------------------------------------------------------
Taehee Yoo (1):
batman-adv: set .owner to THIS_MODULE
net/batman-adv/log.c | 1 +
1 file changed, 1 insertion(+)
^ permalink raw reply
* [PATCH 1/1] batman-adv: set .owner to THIS_MODULE
From: Simon Wunderlich @ 2020-11-24 13:44 UTC (permalink / raw)
To: davem, kuba
Cc: netdev, b.a.t.m.a.n, Taehee Yoo, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20201124134417.17269-1-sw@simonwunderlich.de>
From: Taehee Yoo <ap420073@gmail.com>
If THIS_MODULE is not set, the module would be removed while debugfs is
being used.
It eventually makes kernel panic.
Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol")
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/log.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/batman-adv/log.c b/net/batman-adv/log.c
index a67b2b091447..c0ca5fbe5b08 100644
--- a/net/batman-adv/log.c
+++ b/net/batman-adv/log.c
@@ -180,6 +180,7 @@ static const struct file_operations batadv_log_fops = {
.read = batadv_log_read,
.poll = batadv_log_poll,
.llseek = no_llseek,
+ .owner = THIS_MODULE,
};
/**
--
2.20.1
^ permalink raw reply related
* Re: [PATCH net-next v4 2/7] dpaa_eth: add basic XDP support
From: Maciej Fijalkowski @ 2020-11-24 13:51 UTC (permalink / raw)
To: Camelia Groza
Cc: kuba, brouer, saeed, davem, madalin.bucur, ioana.ciornei, netdev
In-Reply-To: <f93f96e77e905c7cd96660095611ef0944feb8da.1606150838.git.camelia.groza@nxp.com>
On Mon, Nov 23, 2020 at 07:36:20PM +0200, Camelia Groza wrote:
> Implement the XDP_DROP and XDP_PASS actions.
>
> Avoid draining and reconfiguring the buffer pool at each XDP
> setup/teardown by increasing the frame headroom and reserving
> XDP_PACKET_HEADROOM bytes from the start. Since we always reserve an
> entire page per buffer, this change only impacts Jumbo frame scenarios
> where the maximum linear frame size is reduced by 256 bytes. Multi
> buffer Scatter/Gather frames are now used instead in these scenarios.
>
> Allow XDP programs to access the entire buffer.
>
> The data in the received frame's headroom can be overwritten by the XDP
> program. Extract the relevant fields from the headroom while they are
> still available, before running the XDP program.
>
> Since the headroom might be resized before the frame is passed up to the
> stack, remove the check for a fixed headroom value when building an skb.
>
> Allow the meta data to be updated and pass the information up the stack.
>
> Scatter/Gather frames are dropped when XDP is enabled.
>
> Acked-by: Madalin Bucur <madalin.bucur@oss.nxp.com>
> Signed-off-by: Camelia Groza <camelia.groza@nxp.com>
> ---
> Changes in v2:
> - warn only once if extracting the timestamp from a received frame fails
>
> Changes in v3:
> - drop received S/G frames when XDP is enabled
>
> Changes in v4:
> - report a warning if the MTU is too hight for running XDP
> - report an error if opening the device fails in the XDP setup
>
> drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 166 ++++++++++++++++++++++---
> drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 2 +
> 2 files changed, 152 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> index 88533a2..8acce62 100644
> --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> @@ -53,6 +53,8 @@
> #include <linux/dma-mapping.h>
> #include <linux/sort.h>
> #include <linux/phy_fixed.h>
> +#include <linux/bpf.h>
> +#include <linux/bpf_trace.h>
> #include <soc/fsl/bman.h>
> #include <soc/fsl/qman.h>
> #include "fman.h"
> @@ -177,7 +179,7 @@
> #define DPAA_HWA_SIZE (DPAA_PARSE_RESULTS_SIZE + DPAA_TIME_STAMP_SIZE \
> + DPAA_HASH_RESULTS_SIZE)
> #define DPAA_RX_PRIV_DATA_DEFAULT_SIZE (DPAA_TX_PRIV_DATA_SIZE + \
> - dpaa_rx_extra_headroom)
> + XDP_PACKET_HEADROOM - DPAA_HWA_SIZE)
> #ifdef CONFIG_DPAA_ERRATUM_A050385
> #define DPAA_RX_PRIV_DATA_A050385_SIZE (DPAA_A050385_ALIGN - DPAA_HWA_SIZE)
> #define DPAA_RX_PRIV_DATA_SIZE (fman_has_errata_a050385() ? \
> @@ -1733,7 +1735,6 @@ static struct sk_buff *contig_fd_to_skb(const struct dpaa_priv *priv,
> SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
> if (WARN_ONCE(!skb, "Build skb failure on Rx\n"))
> goto free_buffer;
> - WARN_ON(fd_off != priv->rx_headroom);
> skb_reserve(skb, fd_off);
> skb_put(skb, qm_fd_get_length(fd));
>
> @@ -2349,12 +2350,62 @@ static enum qman_cb_dqrr_result rx_error_dqrr(struct qman_portal *portal,
> return qman_cb_dqrr_consume;
> }
>
> +static u32 dpaa_run_xdp(struct dpaa_priv *priv, struct qm_fd *fd, void *vaddr,
> + unsigned int *xdp_meta_len)
> +{
> + ssize_t fd_off = qm_fd_get_offset(fd);
> + struct bpf_prog *xdp_prog;
> + struct xdp_buff xdp;
> + u32 xdp_act;
> +
> + rcu_read_lock();
> +
> + xdp_prog = READ_ONCE(priv->xdp_prog);
> + if (!xdp_prog) {
> + rcu_read_unlock();
> + return XDP_PASS;
> + }
> +
> + xdp.data = vaddr + fd_off;
> + xdp.data_meta = xdp.data;
> + xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM;
> + xdp.data_end = xdp.data + qm_fd_get_length(fd);
> + xdp.frame_sz = DPAA_BP_RAW_SIZE;
> +
> + xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp);
> +
> + /* Update the length and the offset of the FD */
> + qm_fd_set_contig(fd, xdp.data - vaddr, xdp.data_end - xdp.data);
Shouldn't you do this update based on xdp.data_meta, not xdp.data?
> +
> + switch (xdp_act) {
> + case XDP_PASS:
> + *xdp_meta_len = xdp.data - xdp.data_meta;
> + break;
> + default:
> + bpf_warn_invalid_xdp_action(xdp_act);
> + fallthrough;
> + case XDP_ABORTED:
> + trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act);
> + fallthrough;
> + case XDP_DROP:
> + /* Free the buffer */
> + free_pages((unsigned long)vaddr, 0);
> + break;
> + }
> +
> + rcu_read_unlock();
> +
> + return xdp_act;
> +}
> +
> static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal,
> struct qman_fq *fq,
> const struct qm_dqrr_entry *dq,
> bool sched_napi)
> {
> + bool ts_valid = false, hash_valid = false;
> struct skb_shared_hwtstamps *shhwtstamps;
> + unsigned int skb_len, xdp_meta_len = 0;
> struct rtnl_link_stats64 *percpu_stats;
> struct dpaa_percpu_priv *percpu_priv;
> const struct qm_fd *fd = &dq->fd;
> @@ -2362,12 +2413,14 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal,
> enum qm_fd_format fd_format;
> struct net_device *net_dev;
> u32 fd_status, hash_offset;
> + struct qm_sg_entry *sgt;
> struct dpaa_bp *dpaa_bp;
> struct dpaa_priv *priv;
> - unsigned int skb_len;
> struct sk_buff *skb;
> int *count_ptr;
> + u32 xdp_act;
> void *vaddr;
> + u32 hash;
> u64 ns;
>
> fd_status = be32_to_cpu(fd->status);
> @@ -2423,35 +2476,67 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal,
> count_ptr = this_cpu_ptr(dpaa_bp->percpu_count);
> (*count_ptr)--;
>
> - if (likely(fd_format == qm_fd_contig))
> + /* Extract the timestamp stored in the headroom before running XDP */
> + if (priv->rx_tstamp) {
> + if (!fman_port_get_tstamp(priv->mac_dev->port[RX], vaddr, &ns))
> + ts_valid = true;
> + else
> + WARN_ONCE(1, "fman_port_get_tstamp failed!\n");
> + }
> +
> + /* Extract the hash stored in the headroom before running XDP */
> + if (net_dev->features & NETIF_F_RXHASH && priv->keygen_in_use &&
> + !fman_port_get_hash_result_offset(priv->mac_dev->port[RX],
> + &hash_offset)) {
> + hash = be32_to_cpu(*(u32 *)(vaddr + hash_offset));
> + hash_valid = true;
> + }
> +
> + if (likely(fd_format == qm_fd_contig)) {
> + xdp_act = dpaa_run_xdp(priv, (struct qm_fd *)fd, vaddr,
> + &xdp_meta_len);
> + if (xdp_act != XDP_PASS) {
> + percpu_stats->rx_packets++;
> + percpu_stats->rx_bytes += qm_fd_get_length(fd);
> + return qman_cb_dqrr_consume;
> + }
> skb = contig_fd_to_skb(priv, fd);
> - else
> + } else {
> + /* XDP doesn't support S/G frames. Return the fragments to the
> + * buffer pool and release the SGT.
> + */
> + if (READ_ONCE(priv->xdp_prog)) {
> + WARN_ONCE(1, "S/G frames not supported under XDP\n");
> + sgt = vaddr + qm_fd_get_offset(fd);
> + dpaa_release_sgt_members(sgt);
> + free_pages((unsigned long)vaddr, 0);
> + return qman_cb_dqrr_consume;
> + }
> skb = sg_fd_to_skb(priv, fd);
> + }
> if (!skb)
> return qman_cb_dqrr_consume;
>
> - if (priv->rx_tstamp) {
> + if (xdp_meta_len)
> + skb_metadata_set(skb, xdp_meta_len);
> +
> + /* Set the previously extracted timestamp */
> + if (ts_valid) {
> shhwtstamps = skb_hwtstamps(skb);
> memset(shhwtstamps, 0, sizeof(*shhwtstamps));
> -
> - if (!fman_port_get_tstamp(priv->mac_dev->port[RX], vaddr, &ns))
> - shhwtstamps->hwtstamp = ns_to_ktime(ns);
> - else
> - dev_warn(net_dev->dev.parent, "fman_port_get_tstamp failed!\n");
> + shhwtstamps->hwtstamp = ns_to_ktime(ns);
> }
>
> skb->protocol = eth_type_trans(skb, net_dev);
>
> - if (net_dev->features & NETIF_F_RXHASH && priv->keygen_in_use &&
> - !fman_port_get_hash_result_offset(priv->mac_dev->port[RX],
> - &hash_offset)) {
> + /* Set the previously extracted hash */
> + if (hash_valid) {
> enum pkt_hash_types type;
>
> /* if L4 exists, it was used in the hash generation */
> type = be32_to_cpu(fd->status) & FM_FD_STAT_L4CV ?
> PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3;
> - skb_set_hash(skb, be32_to_cpu(*(u32 *)(vaddr + hash_offset)),
> - type);
> + skb_set_hash(skb, hash, type);
> }
>
> skb_len = skb->len;
> @@ -2671,6 +2756,54 @@ static int dpaa_eth_stop(struct net_device *net_dev)
> return err;
> }
>
> +static int dpaa_setup_xdp(struct net_device *net_dev, struct bpf_prog *prog)
> +{
> + struct dpaa_priv *priv = netdev_priv(net_dev);
> + struct bpf_prog *old_prog;
> + int err, max_contig_data;
> + bool up;
> +
> + max_contig_data = priv->dpaa_bp->size - priv->rx_headroom;
> +
> + /* S/G fragments are not supported in XDP-mode */
> + if (prog &&
> + (net_dev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN > max_contig_data)) {
> + dev_warn(net_dev->dev.parent,
> + "The maximum MTU for XDP is %d\n",
> + max_contig_data - VLAN_ETH_HLEN - ETH_FCS_LEN);
> + return -EINVAL;
> + }
> +
> + up = netif_running(net_dev);
> +
> + if (up)
> + dpaa_eth_stop(net_dev);
> +
> + old_prog = xchg(&priv->xdp_prog, prog);
> + if (old_prog)
> + bpf_prog_put(old_prog);
> +
> + if (up) {
> + err = dpaa_open(net_dev);
> + if (err) {
> + dev_err(net_dev->dev.parent, "dpaa_open() failed\n");
> + return err;
So you decided not to take advantage of extack messages?
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int dpaa_xdp(struct net_device *net_dev, struct netdev_bpf *xdp)
> +{
> + switch (xdp->command) {
> + case XDP_SETUP_PROG:
> + return dpaa_setup_xdp(net_dev, xdp->prog);
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static int dpaa_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
> {
> struct dpaa_priv *priv = netdev_priv(dev);
> @@ -2737,6 +2870,7 @@ static int dpaa_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd)
> .ndo_set_rx_mode = dpaa_set_rx_mode,
> .ndo_do_ioctl = dpaa_ioctl,
> .ndo_setup_tc = dpaa_setup_tc,
> + .ndo_bpf = dpaa_xdp,
> };
>
> static int dpaa_napi_add(struct net_device *net_dev)
> diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h
> index da30e5d..94e8613 100644
> --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h
> +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h
> @@ -196,6 +196,8 @@ struct dpaa_priv {
>
> bool tx_tstamp; /* Tx timestamping enabled */
> bool rx_tstamp; /* Rx timestamping enabled */
> +
> + struct bpf_prog *xdp_prog;
> };
>
> /* from dpaa_ethtool.c */
> --
> 1.9.1
>
^ permalink raw reply
* Re: [PATCH] net: stmmac: add flexible PPS to dwmac 4.10a
From: Ahmad Fatoum @ 2020-11-24 14:15 UTC (permalink / raw)
To: Jakub Kicinski, Antonio Borneo
Cc: Giuseppe Cavallaro, Alexandre Torgue, Jose Abreu, David S. Miller,
netdev, Maxime Coquelin, linux-stm32, linux-arm-kernel,
linux-kernel, Pengutronix Kernel Team, has
In-Reply-To: <20191009152618.33b45c2d@cakuba.netronome.com>
Hello Jakub,
On 10.10.19 00:26, Jakub Kicinski wrote:
> On Mon, 7 Oct 2019 17:43:06 +0200, Antonio Borneo wrote:
>> All the registers and the functionalities used in the callback
>> dwmac5_flex_pps_config() are common between dwmac 4.10a [1] and
>> 5.00a [2].
>>
>> Reuse the same callback for dwmac 4.10a too.
>>
>> Tested on STM32MP15x, based on dwmac 4.10a.
>>
>> [1] DWC Ethernet QoS Databook 4.10a October 2014
>> [2] DWC Ethernet QoS Databook 5.00a September 2017
>>
>> Signed-off-by: Antonio Borneo <antonio.borneo@st.com>
>
> Applied to net-next.
This patch seems to have been fuzzily applied at the wrong location.
The diff describes extension of dwmac 4.10a and so does the @@ line:
@@ -864,6 +864,7 @@ const struct stmmac_ops dwmac410_ops = {
The patch was applied mainline as 757926247836 ("net: stmmac: add
flexible PPS to dwmac 4.10a"), but it extends dwmac4_ops instead:
@@ -938,6 +938,7 @@ const struct stmmac_ops dwmac4_ops = {
I don't know if dwmac4 actually supports FlexPPS, so I think it's
better to be on the safe side and revert 757926247836 and add the
change for the correct variant.
Cheers,
Ahmad
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* Re: [PATCH net-next 2/2] net: nfc: s3fwrn5: Support a UART interface
From: krzk @ 2020-11-24 14:15 UTC (permalink / raw)
To: Bongsu Jeon
Cc: Bongsu Jeon, kuba@kernel.org, linux-kernel@vger.kernel.org,
linux-nfc@lists.01.org, netdev@vger.kernel.org
In-Reply-To: <CACwDmQDOm6PAyphMiUFizueENMdW3Bo5PvdP_VC_sfBEHc9pMQ@mail.gmail.com>
On Tue, Nov 24, 2020 at 09:05:52PM +0900, Bongsu Jeon wrote:
> On Mon, Nov 23, 2020 at 5:55 PM krzk@kernel.org <krzk@kernel.org> wrote:
> > > +static enum s3fwrn5_mode s3fwrn82_uart_get_mode(void *phy_id)
> > > +{
> > > + struct s3fwrn82_uart_phy *phy = phy_id;
> > > + enum s3fwrn5_mode mode;
> > > +
> > > + mutex_lock(&phy->mutex);
> > > + mode = phy->mode;
> > > + mutex_unlock(&phy->mutex);
> > > + return mode;
> > > +}
> >
> > All this duplicates I2C version. You need to start either reusing common
> > blocks.
> >
>
> Okay. I will do refactoring on i2c.c and uart.c to make common blocks.
> is it okay to separate a patch for it?
Yes, that would be the best - refactor the driver to split some common
methods and then in next patch add new s3fwrn82 UART driver.
> > > +
> > > +static int s3fwrn82_uart_write(void *phy_id, struct sk_buff *out)
> > > +{
> > > + struct s3fwrn82_uart_phy *phy = phy_id;
> > > + int err;
> > > +
> > > + err = serdev_device_write(phy->ser_dev,
> > > + out->data, out->len,
> > > + MAX_SCHEDULE_TIMEOUT);
> > > + if (err < 0)
> > > + return err;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static const struct s3fwrn5_phy_ops uart_phy_ops = {
> > > + .set_wake = s3fwrn82_uart_set_wake,
> > > + .set_mode = s3fwrn82_uart_set_mode,
> > > + .get_mode = s3fwrn82_uart_get_mode,
> > > + .write = s3fwrn82_uart_write,
> > > +};
> > > +
> > > +static int s3fwrn82_uart_read(struct serdev_device *serdev,
> > > + const unsigned char *data,
> > > + size_t count)
> > > +{
> > > + struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
> > > + size_t i;
> > > +
> > > + for (i = 0; i < count; i++) {
> > > + skb_put_u8(phy->recv_skb, *data++);
> > > +
> > > + if (phy->recv_skb->len < S3FWRN82_NCI_HEADER)
> > > + continue;
> > > +
> > > + if ((phy->recv_skb->len - S3FWRN82_NCI_HEADER)
> > > + < phy->recv_skb->data[S3FWRN82_NCI_IDX])
> > > + continue;
> > > +
> > > + s3fwrn5_recv_frame(phy->ndev, phy->recv_skb, phy->mode);
> > > + phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
> > > + if (!phy->recv_skb)
> > > + return 0;
> > > + }
> > > +
> > > + return i;
> > > +}
> > > +
> > > +static struct serdev_device_ops s3fwrn82_serdev_ops = {
> >
> > const
> >
> > > + .receive_buf = s3fwrn82_uart_read,
> > > + .write_wakeup = serdev_device_write_wakeup,
> > > +};
> > > +
> > > +static const struct of_device_id s3fwrn82_uart_of_match[] = {
> > > + { .compatible = "samsung,s3fwrn82-uart", },
> > > + {},
> > > +};
> > > +MODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match);
> > > +
> > > +static int s3fwrn82_uart_parse_dt(struct serdev_device *serdev)
> > > +{
> > > + struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
> > > + struct device_node *np = serdev->dev.of_node;
> > > +
> > > + if (!np)
> > > + return -ENODEV;
> > > +
> > > + phy->gpio_en = of_get_named_gpio(np, "en-gpios", 0);
> > > + if (!gpio_is_valid(phy->gpio_en))
> > > + return -ENODEV;
> > > +
> > > + phy->gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
> >
> > You should not cast it it unsigned int. I'll fix the s3fwrn5 from which
> > you copied this apparently.
> >
>
> Okay. I will fix it.
>
> > > + if (!gpio_is_valid(phy->gpio_fw_wake))
> > > + return -ENODEV;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int s3fwrn82_uart_probe(struct serdev_device *serdev)
> > > +{
> > > + struct s3fwrn82_uart_phy *phy;
> > > + int ret = -ENOMEM;
> > > +
> > > + phy = devm_kzalloc(&serdev->dev, sizeof(*phy), GFP_KERNEL);
> > > + if (!phy)
> > > + goto err_exit;
> > > +
> > > + phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
> > > + if (!phy->recv_skb)
> > > + goto err_free;
> > > +
> > > + mutex_init(&phy->mutex);
> > > + phy->mode = S3FWRN5_MODE_COLD;
> > > +
> > > + phy->ser_dev = serdev;
> > > + serdev_device_set_drvdata(serdev, phy);
> > > + serdev_device_set_client_ops(serdev, &s3fwrn82_serdev_ops);
> > > + ret = serdev_device_open(serdev);
> > > + if (ret) {
> > > + dev_err(&serdev->dev, "Unable to open device\n");
> > > + goto err_skb;
> > > + }
> > > +
> > > + ret = serdev_device_set_baudrate(serdev, 115200);
> >
> > Why baudrate is fixed?
> >
>
> RN82 NFC chip only supports 115200 baudrate for UART.
OK, I guess it could be extended in the future for other frequencies, if
needed.
>
> > > + if (ret != 115200) {
> > > + ret = -EINVAL;
> > > + goto err_serdev;
> > > + }
> > > +
> > > + serdev_device_set_flow_control(serdev, false);
> > > +
> > > + ret = s3fwrn82_uart_parse_dt(serdev);
> > > + if (ret < 0)
> > > + goto err_serdev;
> > > +
> > > + ret = devm_gpio_request_one(&phy->ser_dev->dev,
> > > + phy->gpio_en,
> > > + GPIOF_OUT_INIT_HIGH,
> > > + "s3fwrn82_en");
> >
> > This is weirdly wrapped.
> >
>
> Did you ask about devem_gpio_request_one function's parenthesis and parameters?
> If it is right, I changed it after i ran the checkpatch.pl --strict and
> i saw message like the alignment should match open parenthesis.
Yeah, but it does not mean to wrap after each argument. It should be
something like:
ret = devm_gpio_request_one(&phy->ser_dev->dev, phy->gpio_en,
GPIOF_OUT_INIT_HIGH, "s3fwrn82_en");
>
> > > + if (ret < 0)
> > > + goto err_serdev;
> > > +
> > > + ret = devm_gpio_request_one(&phy->ser_dev->dev,
> > > + phy->gpio_fw_wake,
> > > + GPIOF_OUT_INIT_LOW,
> > > + "s3fwrn82_fw_wake");
> > > + if (ret < 0)
> > > + goto err_serdev;
> > > +
> > > + ret = s3fwrn5_probe(&phy->ndev, phy, &phy->ser_dev->dev, &uart_phy_ops);
> > > + if (ret < 0)
> > > + goto err_serdev;
> > > +
> > > + return ret;
> > > +
> > > +err_serdev:
> > > + serdev_device_close(serdev);
> > > +err_skb:
> > > + kfree_skb(phy->recv_skb);
> > > +err_free:
> > > + kfree(phy);
> >
> > Eee.... why? Did you test this code?
> >
>
> I didn't test this code. i just added this code as defense code.
> If the error happens, then allocated memory and device will be free
> according to the fail case.
Really, this won't work. It's kind of obvious why... You cannot use
kfree() on memory which is not allocated with kzalloc(). Or IOW, you
cannot use it if it is being freed by devm.
I doubt that you tested either this or the remove callback because if
you did test it, you would see easily:
[ 145.018200] Unable to handle kernel paging request at virtual address ffffffed
[ 145.025428] pgd = 67e71ef8
[ 145.027774] [ffffffed] *pgd=6fffd861, *pte=00000000, *ppte=00000000
[ 145.034052] Internal error: Oops: 837 [#1] PREEMPT SMP ARM
[ 145.039278] Modules linked in: s5p_mfc exynos_gsc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc
[ 145.052987] CPU: 2 PID: 325 Comm: bash Tainted: G W 5.10.0-rc3-next-20201113-00008-g62dd0da04641-dirty #79
[ 145.063883] Hardware name: Samsung Exynos (Flattened Device Tree)
[ 145.069971] PC is at devres_remove+0x9c/0xb4
[ 145.074180] LR is at devres_remove+0x78/0xb4
[ 145.078418] pc : [<c06a6134>] lr : [<c06a6110>] psr: a0010093
[ 145.084666] sp : c6af7de0 ip : 00000001 fp : c2dd16e4
[ 145.089861] r10: c2dd16c0 r9 : 60010013 r8 : c05894a8
[ 145.095060] r7 : c058b128 r6 : c4bbc100 r5 : c2dd1410 r4 : c440d5c0
[ 145.101564] r3 : ffffffed r2 : c4bbc100 r1 : 60010013 r0 : c2dd16c0
[ 145.108068] Flags: NzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment none
[ 145.115260] Control: 10c5387d Table: 46aa006a DAC: 00000051
[ 145.120979] Process bash (pid: 325, stack limit = 0x97d30bf6)
[ 145.126696] Stack: (0xc6af7de0 to 0xc6af8000)
[ 145.131039] 7de0: c4bbc100 c2dd1410 c058b128 c2dd1410 c440d5c0 c39a3400 c1305f08 c1348d28
[ 145.139185] 7e00: 0000002b c06a6a54 c2dd1410 00000000 c440d5c0 c058ab94 c2dd1410 c06ccd04
[ 145.147333] 7e20: c2dd1410 c19ab618 00000000 c19ab620 c39a3400 c06a1ff8 00000000 c2dd1524
[ 145.155476] 7e40: c1c16800 c2dd1410 c1305f08 c1305f08 c1348d28 c39a3400 c6af7f78 c39a3410
[ 145.163621] 7e60: c1c16800 c06a2624 c1305f08 c1305f08 0000000c c2dd1410 00000000 c1305f08
[ 145.171769] 7e80: 0000000c c39a3400 c6af7f78 c39a3410 c1c16800 c06a29ec c2dd1410 c1305f08
[ 145.179914] 7ea0: c12f0410 c06a0728 0000000c c4bbc140 00000000 00000000 c39a3400 c0381968
[ 145.188058] 7ec0: 00000000 00000000 00000000 c5164640 0000000c 005e0da0 c6af7f78 c02d6120
[ 145.196202] 7ee0: c038185c c02d5b70 00000001 00000000 c02d6120 00000000 00000000 c133c27a
[ 145.204353] 7f00: c39f5690 c127b15c c116e588 c0b40ef0 c133c14f c3af4450 c39f5670 00000000
[ 145.212497] 7f20: c3af4450 c018ea90 c120958c c39f5670 c3af4450 c116e588 c02f9988 c0197a00
[ 145.220639] 7f40: c116e588 c0b40ef0 00000003 c1208ec8 00000000 c5164640 c5164640 00000000
[ 145.228784] 7f60: 00000000 005e0da0 0000000c 00000004 00000000 c02d6120 00000000 00000000
[ 145.236931] 7f80: 00000006 c1208ec8 c5164640 0000000c 005e0da0 b6fec680 00000004 c0100244
[ 145.245077] 7fa0: c6af6000 c0100060 0000000c 005e0da0 00000001 005e0da0 0000000c 00000000
[ 145.253222] 7fc0: 0000000c 005e0da0 b6fec680 00000004 b6f6310c b6f62c74 00000000 00000000
[ 145.261370] 7fe0: 00000498 bef78290 b6e84ea4 b6ee2300 60010010 00000001 00000000 00000000
[ 145.269555] [<c06a6134>] (devres_remove) from [<c06a6a54>] (devres_release+0x10/0x3c)
[ 145.277340] [<c06a6a54>] (devres_release) from [<c058ab94>] (devm_pinctrl_put+0x20/0x44)
[ 145.285388] [<c058ab94>] (devm_pinctrl_put) from [<c06ccd04>] (pinctrl_bind_pins+0xd0/0x27c)
[ 145.293786] [<c06ccd04>] (pinctrl_bind_pins) from [<c06a1ff8>] (really_probe+0x90/0x4f0)
[ 145.301842] [<c06a1ff8>] (really_probe) from [<c06a2624>] (driver_probe_device+0x78/0x1e0)
[ 145.310069] [<c06a2624>] (driver_probe_device) from [<c06a29ec>] (device_driver_attach+0x58/0x60)
[ 145.318936] [<c06a29ec>] (device_driver_attach) from [<c06a0728>] (bind_store+0x8c/0x100)
[ 145.327074] [<c06a0728>] (bind_store) from [<c0381968>] (kernfs_fop_write+0x10c/0x230)
[ 145.334960] [<c0381968>] (kernfs_fop_write) from [<c02d5b70>] (vfs_write+0xcc/0x524)
[ 145.342658] [<c02d5b70>] (vfs_write) from [<c02d6120>] (ksys_write+0x64/0xf0)
[ 145.349759] [<c02d6120>] (ksys_write) from [<c0100060>] (ret_fast_syscall+0x0/0x2c)
[ 145.357377] Exception stack(0xc6af7fa8 to 0xc6af7ff0)
[ 145.362395] 7fa0: 0000000c 005e0da0 00000001 005e0da0 0000000c 00000000
[ 145.370551] 7fc0: 0000000c 005e0da0 b6fec680 00000004 b6f6310c b6f62c74 00000000 00000000
[ 145.378699] 7fe0: 00000498 bef78290 b6e84ea4 b6ee2300
[ 145.383719] Code: e1a0000a e5942000 e1a01009 e5823004 (e5832000)
[ 145.389795] ---[ end trace 769f050185612ec3 ]---
Please fix the double-free.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v2] brmcfmac: fix compile when DEBUG is defined
From: hby @ 2020-11-24 14:24 UTC (permalink / raw)
To: kvalo, davem, kuba; +Cc: linux-wireless, netdev, linux-kernel, hby
The steps:
1. add "#define DEBUG" in drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c line 61.
2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- O=../Out_Linux bcm2835_defconfig
3. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- O=../Out_Linux/ zImage modules dtbs -j8
Then, it will fail, the compile log described below:
Kernel: arch/arm/boot/zImage is ready
MODPOST Module.symvers
ERROR: modpost: "brcmf_debugfs_add_entry" [drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko] undefined!
ERROR: modpost: "brcmf_debugfs_get_devdir" [drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko] undefined!
ERROR: modpost: "__brcmf_dbg" [drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko] undefined!
scripts/Makefile.modpost:111: recipe for target 'Module.symvers' failed
make[2]: *** [Module.symvers] Error 1
make[2]: *** Deleting file 'Module.symvers'
Makefile:1390: recipe for target 'modules' failed
make[1]: *** [modules] Error 2
make[1]: Leaving directory '/home/hby/gitee/linux_origin/Out_Linux'
Makefile:185: recipe for target '__sub-make' failed
make: *** [__sub-make] Error 2
Signed-off-by: hby <hby2003@163.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
index 4146faeed..c2eb3aa67 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
@@ -60,7 +60,7 @@ void __brcmf_err(struct brcmf_bus *bus, const char *func, const char *fmt, ...);
##__VA_ARGS__); \
} while (0)
-#if defined(DEBUG) || defined(CONFIG_BRCM_TRACING)
+#if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG)
/* For debug/tracing purposes treat info messages as errors */
#define brcmf_info brcmf_err
@@ -114,7 +114,7 @@ extern int brcmf_msg_level;
struct brcmf_bus;
struct brcmf_pub;
-#ifdef DEBUG
+#if defined(CONFIG_BRCMDBG)
struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
void brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
int (*read_fn)(struct seq_file *seq, void *data));
--
2.17.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox