public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver
@ 2024-11-20 10:56 Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 03/21] motorcomm:yt6801: Implement the fxgmac_drv_probe function Frank Sae
                   ` (15 more replies)
  0 siblings, 16 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

This series includes adding Motorcomm YT6801 Gigabit ethernet driver
and adding yt6801 ethernet driver entry in MAINTAINERS file.

YT6801 integrates a YT8531S phy.

v1 -> v2:
- Split this driver into multiple patches.
- Reorganize this driver code and remove redundant code
- Remove PHY handling code and use phylib.
- Remove writing ASPM config
- Use generic power management instead of pci_driver.suspend()/resume()
- Add Space before closing "*/"

Frank Sae (21):
  motorcomm:yt6801: Add support for a pci table in this module
  motorcomm:yt6801: Implement pci_driver shutdown
  motorcomm:yt6801: Implement the fxgmac_drv_probe function
  motorcomm:yt6801: Implement the .ndo_open function
  motorcomm:yt6801: Implement the fxgmac_start function
  motorcomm:yt6801: Implement the poll functions
  motorcomm:yt6801: Implement the fxgmac_init function
  motorcomm:yt6801: Implement the fxgmac_read_mac_addr function
  motorcomm:yt6801: Implement some hw_ops function
  motorcomm:yt6801: Implement .ndo_start_xmit function
  motorcomm:yt6801: Implement some net_device_ops function
  motorcomm:yt6801: Implement .ndo_tx_timeout and .ndo_change_mtu
    functions
  motorcomm:yt6801: Implement some ethtool_ops function
  motorcomm:yt6801: Implement the WOL function of ethtool_ops
  motorcomm:yt6801: Implement pci_driver suspend and resume
  motorcomm:yt6801: Add a Makefile in the motorcomm folder
  motorcomm:yt6801: Update the Makefile and Kconfig in the motorcomm
  motorcomm:yt6801: Update the Makefile and Kconfig in the ethernet
  ethernet: Update the index.rst in the ethernet documentation folder
  motorcomm:yt6801: Add a yt6801.rst in the ethernet documentation
    folder
  MAINTAINERS:Add the motorcomm ethernet driver entry

 .../device_drivers/ethernet/index.rst         |    1 +
 .../ethernet/motorcomm/yt6801.rst             |   20 +
 MAINTAINERS                                   |    8 +
 drivers/net/ethernet/Kconfig                  |    1 +
 drivers/net/ethernet/Makefile                 |    1 +
 drivers/net/ethernet/motorcomm/Kconfig        |   27 +
 drivers/net/ethernet/motorcomm/Makefile       |    6 +
 .../net/ethernet/motorcomm/yt6801/Makefile    |    9 +
 .../net/ethernet/motorcomm/yt6801/yt6801.h    |  617 +++
 .../ethernet/motorcomm/yt6801/yt6801_desc.c   |  638 ++++
 .../ethernet/motorcomm/yt6801/yt6801_desc.h   |   39 +
 .../motorcomm/yt6801/yt6801_ethtool.c         |  907 +++++
 .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 3383 +++++++++++++++++
 .../ethernet/motorcomm/yt6801/yt6801_net.c    | 2908 ++++++++++++++
 .../ethernet/motorcomm/yt6801/yt6801_net.h    |   32 +
 .../ethernet/motorcomm/yt6801/yt6801_pci.c    |  191 +
 .../ethernet/motorcomm/yt6801/yt6801_type.h   | 1398 +++++++
 17 files changed, 10186 insertions(+)
 create mode 100644 Documentation/networking/device_drivers/ethernet/motorcomm/yt6801.rst
 create mode 100644 drivers/net/ethernet/motorcomm/Kconfig
 create mode 100644 drivers/net/ethernet/motorcomm/Makefile
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/Makefile
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801.h
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.h
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_net.h
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_type.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 03/21] motorcomm:yt6801: Implement the fxgmac_drv_probe function
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 04/21] motorcomm:yt6801: Implement the .ndo_open function Frank Sae
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement the fxgmac_drv_probe function to init interrupts, register mdio and
netdev.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../ethernet/motorcomm/yt6801/yt6801_net.c    | 193 ++++++++++++++++++
 1 file changed, 193 insertions(+)

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
index 925bc1e7d..0cb2808b7 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
@@ -249,3 +249,196 @@ int fxgmac_net_powerdown(struct fxgmac_pdata *pdata, bool wake_en)
 
 	return 0;
 }
+
+#ifdef CONFIG_PCI_MSI
+static void fxgmac_init_interrupt_scheme(struct fxgmac_pdata *pdata)
+{
+	struct pci_dev *pdev = to_pci_dev(pdata->dev);
+	u32 i, *flags = &pdata->int_flags;
+	int vectors, rc, req_vectors;
+
+	/* Since we have 4 channels, we must ensure the number of cpu core > 4
+	 * otherwise, just roll back to legacy
+	 *  0-3 for rx, 4 for tx, 5 for misc
+	 */
+	vectors = num_online_cpus();
+	if (vectors < FXGMAC_MAX_DMA_RX_CHANNELS)
+		goto enable_msi_interrupt;
+
+	req_vectors = FXGMAC_MSIX_INT_NUMS;
+	pdata->msix_entries =
+		kcalloc(req_vectors, sizeof(struct msix_entry), GFP_KERNEL);
+	if (!pdata->msix_entries)
+		goto enable_msi_interrupt;
+
+	for (i = 0; i < req_vectors; i++)
+		pdata->msix_entries[i].entry = i;
+
+	rc = pci_enable_msix_exact(pdev, pdata->msix_entries, req_vectors);
+	if (rc < 0) {
+		yt_err(pdata, "enable MSIx err, clear msix entries.\n");
+		/* Roll back to msi */
+		kfree(pdata->msix_entries);
+		pdata->msix_entries = NULL;
+		req_vectors = 0;
+		goto enable_msi_interrupt;
+	}
+
+	yt_dbg(pdata, "enable MSIx ok, cpu=%d,vectors=%d.\n", vectors,
+	       req_vectors);
+	fxgmac_set_bits(flags, FXGMAC_FLAG_INTERRUPT_POS,
+			FXGMAC_FLAG_INTERRUPT_LEN,
+			FXGMAC_FLAG_MSIX_ENABLED);
+	pdata->per_channel_irq = 1;
+	pdata->misc_irq = pdata->msix_entries[MSI_ID_PHY_OTHER].vector;
+	return;
+
+enable_msi_interrupt:
+	rc = pci_enable_msi(pdev);
+	if (rc < 0) {
+		fxgmac_set_bits(flags, FXGMAC_FLAG_INTERRUPT_POS,
+				FXGMAC_FLAG_INTERRUPT_LEN,
+				FXGMAC_FLAG_LEGACY_ENABLED);
+		yt_err(pdata, "MSI err, rollback to LEGACY.\n");
+	} else {
+		fxgmac_set_bits(flags, FXGMAC_FLAG_INTERRUPT_POS,
+				FXGMAC_FLAG_INTERRUPT_LEN,
+				FXGMAC_FLAG_MSI_ENABLED);
+		pdata->dev_irq = pdev->irq;
+		yt_dbg(pdata, "enable MSI ok, cpu=%d, irq=%d.\n", vectors,
+		       pdev->irq);
+	}
+}
+#endif
+
+static int fxgmac_mdio_write_reg(struct mii_bus *mii_bus, int phyaddr,
+				 int phyreg, u16 val)
+{
+	struct fxgmac_pdata *yt = mii_bus->priv;
+
+	if (phyaddr > 0)
+		return -ENODEV;
+
+	return yt->hw_ops.write_phy_reg(yt, phyreg, val);
+}
+
+static int fxgmac_mdio_read_reg(struct mii_bus *mii_bus, int phyaddr, int phyreg)
+{
+	struct fxgmac_pdata *yt = mii_bus->priv;
+
+	if (phyaddr > 0)
+		return -ENODEV;
+
+	return  yt->hw_ops.read_phy_reg(yt, phyreg);
+}
+
+static int fxgmac_mdio_register(struct fxgmac_pdata *pdata)
+{
+	struct pci_dev *pdev = to_pci_dev(pdata->dev);
+	struct phy_device *phydev;
+	struct mii_bus *new_bus;
+	int ret;
+
+	new_bus = devm_mdiobus_alloc(&pdev->dev);
+	if (!new_bus) {
+		yt_err(pdata, "devm_mdiobus_alloc err\n");
+		return -ENOMEM;
+	}
+
+	new_bus->name = "yt6801";
+	new_bus->priv = pdata;
+	new_bus->parent = &pdev->dev;
+	new_bus->irq[0] = PHY_MAC_INTERRUPT;
+	snprintf(new_bus->id, MII_BUS_ID_SIZE, "yt6801-%x-%x",
+		 pci_domain_nr(pdev->bus), pci_dev_id(pdev));
+
+	new_bus->read = fxgmac_mdio_read_reg;
+	new_bus->write = fxgmac_mdio_write_reg;
+
+	ret = devm_mdiobus_register(&pdev->dev, new_bus);
+	if (ret < 0) {
+		yt_err(pdata, "devm_mdiobus_register err:%x\n", ret);
+		return ret;
+	}
+
+	phydev = mdiobus_get_phy(new_bus, 0);
+	if (!phydev) {
+		yt_err(pdata, "mdiobus_get_phy err\n");
+		return -ENODEV;
+	}
+
+	pdata->phydev = phydev;
+	phydev->mac_managed_pm = true;
+	phy_support_asym_pause(phydev);
+
+	/* PHY will be woken up in rtl_open() */
+	phy_suspend(phydev);
+
+	return 0;
+}
+
+int fxgmac_drv_probe(struct device *dev, struct fxgmac_resources *res)
+{
+	struct fxgmac_hw_ops *hw_ops;
+	struct fxgmac_pdata *pdata;
+	struct net_device *netdev;
+	int ret;
+
+	netdev = alloc_etherdev_mq(sizeof(struct fxgmac_pdata),
+				   FXGMAC_MAX_DMA_RX_CHANNELS);
+	if (!netdev) {
+		dev_err(dev, "alloc_etherdev_mq err\n");
+		return -ENOMEM;
+	}
+
+	SET_NETDEV_DEV(netdev, dev);
+	pdata = netdev_priv(netdev);
+
+	pdata->dev = dev;
+	pdata->netdev = netdev;
+	pdata->dev_irq = res->irq;
+	pdata->hw_addr = res->addr;
+	pdata->msg_enable = NETIF_MSG_DRV;
+	pdata->dev_state = FXGMAC_DEV_PROBE;
+
+	/* Default to legacy interrupt */
+	fxgmac_set_bits(&pdata->int_flags, FXGMAC_FLAG_INTERRUPT_POS,
+			FXGMAC_FLAG_INTERRUPT_LEN, FXGMAC_FLAG_LEGACY_ENABLED);
+	pdata->misc_irq = pdata->dev_irq;
+	pci_set_drvdata(to_pci_dev(pdata->dev), pdata);
+
+#ifdef CONFIG_PCI_MSI
+	fxgmac_init_interrupt_scheme(pdata);
+#endif
+
+	ret = fxgmac_init(pdata, true);
+	if (ret < 0) {
+		yt_err(pdata, "fxgmac_init err:%d\n", ret);
+		goto err_free_netdev;
+	}
+
+	hw_ops = &pdata->hw_ops;
+	hw_ops->reset_phy(pdata);
+	hw_ops->release_phy(pdata);
+	ret = fxgmac_mdio_register(pdata);
+	if (ret < 0) {
+		yt_err(pdata, "fxgmac_mdio_register err:%d\n", ret);
+		goto err_free_netdev;
+	}
+
+	netif_carrier_off(netdev);
+	ret = register_netdev(netdev);
+	if (ret) {
+		yt_err(pdata, "register_netdev err:%d\n", ret);
+		goto err_free_netdev;
+	}
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "%s, netdev num_tx_q=%u\n", __func__,
+		       netdev->real_num_tx_queues);
+
+	return 0;
+
+err_free_netdev:
+	free_netdev(netdev);
+	return ret;
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 04/21] motorcomm:yt6801: Implement the .ndo_open function
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 03/21] motorcomm:yt6801: Implement the fxgmac_drv_probe function Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 05/21] motorcomm:yt6801: Implement the fxgmac_start function Frank Sae
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement the .ndo_open function to Calculate the Rx buffer size, allocate the
channels and rings.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../ethernet/motorcomm/yt6801/yt6801_desc.c   | 273 ++++++++++++++++++
 .../ethernet/motorcomm/yt6801/yt6801_net.c    |  99 +++++++
 2 files changed, 372 insertions(+)

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c
index 476cf6633..2edf53d9b 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c
@@ -47,3 +47,276 @@ void fxgmac_desc_data_unmap(struct fxgmac_pdata *pdata,
 
 	desc_data->mapped_as_page = 0;
 }
+
+static void fxgmac_ring_free(struct fxgmac_pdata *pdata,
+			     struct fxgmac_ring *ring)
+{
+	if (!ring)
+		return;
+
+	if (ring->desc_data_head) {
+		for (u32 i = 0; i < ring->dma_desc_count; i++)
+			fxgmac_desc_data_unmap(pdata,
+					       FXGMAC_GET_DESC_DATA(ring, i));
+
+		kfree(ring->desc_data_head);
+		ring->desc_data_head = NULL;
+	}
+
+	if (ring->rx_hdr_pa.pages) {
+		dma_unmap_page(pdata->dev, ring->rx_hdr_pa.pages_dma,
+			       ring->rx_hdr_pa.pages_len, DMA_FROM_DEVICE);
+		put_page(ring->rx_hdr_pa.pages);
+
+		ring->rx_hdr_pa.pages = NULL;
+		ring->rx_hdr_pa.pages_len = 0;
+		ring->rx_hdr_pa.pages_offset = 0;
+		ring->rx_hdr_pa.pages_dma = 0;
+	}
+
+	if (ring->rx_buf_pa.pages) {
+		dma_unmap_page(pdata->dev, ring->rx_buf_pa.pages_dma,
+			       ring->rx_buf_pa.pages_len, DMA_FROM_DEVICE);
+		put_page(ring->rx_buf_pa.pages);
+
+		ring->rx_buf_pa.pages = NULL;
+		ring->rx_buf_pa.pages_len = 0;
+		ring->rx_buf_pa.pages_offset = 0;
+		ring->rx_buf_pa.pages_dma = 0;
+	}
+	if (ring->dma_desc_head) {
+		dma_free_coherent(pdata->dev, (sizeof(struct fxgmac_dma_desc) *
+				  ring->dma_desc_count), ring->dma_desc_head,
+				  ring->dma_desc_head_addr);
+		ring->dma_desc_head = NULL;
+	}
+}
+
+static int fxgmac_ring_init(struct fxgmac_pdata *pdata,
+			    struct fxgmac_ring *ring,
+			    unsigned int dma_desc_count)
+{
+	/* Descriptors */
+	ring->dma_desc_count = dma_desc_count;
+	ring->dma_desc_head =
+		dma_alloc_coherent(pdata->dev, (sizeof(struct fxgmac_dma_desc) *
+				   dma_desc_count),
+				   &ring->dma_desc_head_addr, GFP_KERNEL);
+	if (!ring->dma_desc_head)
+		return -ENOMEM;
+
+	/* Array of descriptor data */
+	ring->desc_data_head = kcalloc(dma_desc_count,
+				       sizeof(struct fxgmac_desc_data),
+				       GFP_KERNEL);
+	if (!ring->desc_data_head)
+		return -ENOMEM;
+
+	yt_dbg(pdata,
+	       "dma_desc_head=%p, dma_desc_head_addr=%pad, desc_data_head=%p\n",
+	       ring->dma_desc_head, &ring->dma_desc_head_addr,
+	       ring->desc_data_head);
+	return 0;
+}
+
+static void fxgmac_rings_free(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+
+	fxgmac_ring_free(pdata, channel->tx_ring);
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++)
+		fxgmac_ring_free(pdata, channel->rx_ring);
+}
+
+static int fxgmac_rings_alloc(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	int ret;
+
+	yt_dbg(pdata, "%s - Tx ring:\n", channel->name);
+	ret = fxgmac_ring_init(pdata, channel->tx_ring,
+			       pdata->tx_desc_count);
+	if (ret < 0) {
+		yt_err(pdata, "error initializing Tx ring");
+		goto err_init_ring;
+	}
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata,
+		       "%s, ch=%u,tx_desc_cnt=%u\n",
+		       __func__, 0, pdata->tx_desc_count);
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		yt_dbg(pdata, "%s - Rx ring:\n", channel->name);
+		ret = fxgmac_ring_init(pdata, channel->rx_ring,
+				       pdata->rx_desc_count);
+		if (ret < 0) {
+			yt_err(pdata, "error initializing Rx ring\n");
+			goto err_init_ring;
+		}
+		if (netif_msg_drv(pdata))
+			yt_dbg(pdata,
+			       "%s, ch=%u,rx_desc_cnt=%u\n",
+			       __func__, i,
+			       pdata->rx_desc_count);
+	}
+
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "%s ok\n", __func__);
+
+	return 0;
+
+err_init_ring:
+	fxgmac_rings_free(pdata);
+	return ret;
+}
+
+static void fxgmac_channels_free(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "free_channels,tx_ring=%p\n", channel->tx_ring);
+
+	kfree(channel->tx_ring);
+	channel->tx_ring = NULL;
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "free_channels,rx_ring=%p\n", channel->rx_ring);
+
+	kfree(channel->rx_ring);
+	channel->rx_ring = NULL;
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "free_channels,channel=%p\n", channel);
+
+	kfree(channel);
+	pdata->channel_head = NULL;
+}
+
+#ifdef CONFIG_PCI_MSI
+static void fxgmac_set_msix_tx_irq(struct fxgmac_pdata *pdata,
+				   struct fxgmac_channel *channel, u32 i)
+{
+	if (!FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i))
+		return;
+
+	pdata->channel_irq[FXGMAC_MAX_DMA_RX_CHANNELS] =
+		pdata->msix_entries[FXGMAC_MAX_DMA_RX_CHANNELS].vector;
+	channel->dma_irq_tx = pdata->channel_irq[FXGMAC_MAX_DMA_RX_CHANNELS];
+
+	yt_dbg(pdata, "%s, for MSIx, channel %d dma_irq_tx=%u\n", __func__, i,
+	       channel->dma_irq_tx);
+}
+#endif
+
+static int fxgmac_channels_alloc(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel_head, *channel;
+	struct fxgmac_ring *tx_ring, *rx_ring;
+	int ret = -ENOMEM;
+
+	channel_head = kcalloc(pdata->channel_count,
+			       sizeof(struct fxgmac_channel), GFP_KERNEL);
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "alloc_channels,channel_head=%p,size=%d*%ld\n",
+		       channel_head, pdata->channel_count,
+		       sizeof(struct fxgmac_channel));
+
+	if (!channel_head)
+		return ret;
+
+	tx_ring = kcalloc(FXGMAC_TX_1_RING, sizeof(struct fxgmac_ring),
+			  GFP_KERNEL);
+	if (!tx_ring)
+		goto err_tx_ring;
+
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "alloc_channels,tx_ring=%p,size=%d*%ld\n",
+		       tx_ring, FXGMAC_TX_1_RING,
+		       sizeof(struct fxgmac_ring));
+
+	rx_ring = kcalloc(pdata->rx_ring_count, sizeof(struct fxgmac_ring),
+			  GFP_KERNEL);
+	if (!rx_ring)
+		goto err_rx_ring;
+
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "alloc_channels,rx_ring=%p,size=%d*%ld\n",
+		       rx_ring, pdata->rx_ring_count,
+		       sizeof(struct fxgmac_ring));
+
+	channel = channel_head;
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		snprintf(channel->name, sizeof(channel->name), "channel-%u", i);
+		channel->pdata = pdata;
+		channel->queue_index = i;
+		channel->dma_reg_offset = DMA_CH_BASE + (DMA_CH_INC * i);
+
+		if (pdata->per_channel_irq) {
+			pdata->channel_irq[i] = pdata->msix_entries[i].vector;
+#ifdef CONFIG_PCI_MSI
+			fxgmac_set_msix_tx_irq(pdata, channel, i);
+#endif
+
+			/* Get the per DMA rx interrupt */
+			ret = pdata->channel_irq[i];
+			if (ret < 0) {
+				yt_err(pdata, "get_irq %u err\n", i + 1);
+				goto err_irq;
+			}
+
+			channel->dma_irq_rx = ret;
+			yt_dbg(pdata,
+			       "%s, for MSIx, channel %d dma_irq_rx=%u\n",
+			       __func__, i, channel->dma_irq_rx);
+		}
+
+		if (i < FXGMAC_TX_1_RING)
+			channel->tx_ring = tx_ring++;
+
+		if (i < pdata->rx_ring_count)
+			channel->rx_ring = rx_ring++;
+	}
+
+	pdata->channel_head = channel_head;
+
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "%s ok\n", __func__);
+	return 0;
+
+err_irq:
+	kfree(rx_ring);
+
+err_rx_ring:
+	kfree(tx_ring);
+
+err_tx_ring:
+	kfree(channel_head);
+
+	yt_err(pdata, "%s err:%d\n", __func__, ret);
+	return ret;
+}
+
+void fxgmac_channels_rings_free(struct fxgmac_pdata *pdata)
+{
+	fxgmac_rings_free(pdata);
+	fxgmac_channels_free(pdata);
+}
+
+int fxgmac_channels_rings_alloc(struct fxgmac_pdata *pdata)
+{
+	int ret;
+
+	ret = fxgmac_channels_alloc(pdata);
+	if (ret < 0)
+		goto err_alloc;
+
+	ret = fxgmac_rings_alloc(pdata);
+	if (ret < 0)
+		goto err_alloc;
+
+	return 0;
+
+err_alloc:
+	fxgmac_channels_rings_free(pdata);
+	return ret;
+}
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
index 0cb2808b7..39b91cc26 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
@@ -11,6 +11,26 @@
 #include "yt6801_desc.h"
 #include "yt6801_net.h"
 
+static int fxgmac_calc_rx_buf_size(struct fxgmac_pdata *pdata, unsigned int mtu)
+{
+	u32 rx_buf_size, max_mtu;
+
+	max_mtu = FXGMAC_JUMBO_PACKET_MTU - ETH_HLEN;
+	if (mtu > max_mtu) {
+		yt_err(pdata, "MTU exceeds maximum supported value\n");
+		return -EINVAL;
+	}
+
+	rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+	rx_buf_size =
+		clamp_val(rx_buf_size, FXGMAC_RX_MIN_BUF_SIZE, PAGE_SIZE * 4);
+
+	rx_buf_size = (rx_buf_size + FXGMAC_RX_BUF_ALIGN - 1) &
+		      ~(FXGMAC_RX_BUF_ALIGN - 1);
+
+	return rx_buf_size;
+}
+
 #define FXGMAC_NAPI_ENABLE			0x1
 #define FXGMAC_NAPI_DISABLE			0x0
 static void fxgmac_napi_disable(struct fxgmac_pdata *pdata)
@@ -197,6 +217,36 @@ void fxgmac_stop(struct fxgmac_pdata *pdata)
 	netdev_tx_reset_queue(txq);
 }
 
+void fxgmac_restart(struct fxgmac_pdata *pdata)
+{
+	int ret;
+
+	/* If not running, "restart" will happen on open */
+	if (!netif_running(pdata->netdev) &&
+	    pdata->dev_state != FXGMAC_DEV_START)
+		return;
+
+	mutex_lock(&pdata->mutex);
+	fxgmac_stop(pdata);
+	fxgmac_free_tx_data(pdata);
+	fxgmac_free_rx_data(pdata);
+	ret = fxgmac_start(pdata);
+	if (ret < 0)
+		yt_err(pdata, "%s err.\n", __func__);
+
+	mutex_unlock(&pdata->mutex);
+}
+
+static void fxgmac_restart_work(struct work_struct *work)
+{
+	struct fxgmac_pdata *pdata =
+		container_of(work, struct fxgmac_pdata, restart_work);
+
+	rtnl_lock();
+	fxgmac_restart(pdata);
+	rtnl_unlock();
+}
+
 int fxgmac_net_powerdown(struct fxgmac_pdata *pdata, bool wake_en)
 {
 	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
@@ -250,6 +300,46 @@ int fxgmac_net_powerdown(struct fxgmac_pdata *pdata, bool wake_en)
 	return 0;
 }
 
+static int fxgmac_open(struct net_device *netdev)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	int ret;
+
+	mutex_lock(&pdata->mutex);
+	pdata->dev_state = FXGMAC_DEV_OPEN;
+
+	/* Calculate the Rx buffer size before allocating rings */
+	ret = fxgmac_calc_rx_buf_size(pdata, netdev->mtu);
+	if (ret < 0)
+		goto unlock;
+	pdata->rx_buf_size = ret;
+
+	/* Allocate the channels and rings */
+	ret = fxgmac_channels_rings_alloc(pdata);
+	if (ret < 0)
+		goto unlock;
+
+	INIT_WORK(&pdata->restart_work, fxgmac_restart_work);
+
+	ret = fxgmac_start(pdata);
+	if (ret < 0)
+		goto err_channels_and_rings;
+
+	mutex_unlock(&pdata->mutex);
+
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "%s ok\n", __func__);
+
+	return 0;
+
+err_channels_and_rings:
+	fxgmac_channels_rings_free(pdata);
+	yt_dbg(pdata, "%s, channel alloc err\n", __func__);
+unlock:
+	mutex_unlock(&pdata->mutex);
+	return ret;
+}
+
 #ifdef CONFIG_PCI_MSI
 static void fxgmac_init_interrupt_scheme(struct fxgmac_pdata *pdata)
 {
@@ -442,3 +532,12 @@ int fxgmac_drv_probe(struct device *dev, struct fxgmac_resources *res)
 	free_netdev(netdev);
 	return ret;
 }
+
+static const struct net_device_ops fxgmac_netdev_ops = {
+	.ndo_open		= fxgmac_open,
+};
+
+const struct net_device_ops *fxgmac_get_netdev_ops(void)
+{
+	return &fxgmac_netdev_ops;
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 05/21] motorcomm:yt6801: Implement the fxgmac_start function
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 03/21] motorcomm:yt6801: Implement the fxgmac_drv_probe function Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 04/21] motorcomm:yt6801: Implement the .ndo_open function Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 08/21] motorcomm:yt6801: Implement the fxgmac_read_mac_addr function Frank Sae
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement the fxgmac_start function to connect phy and init phy lib, enable napi
, phy and msix irq.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../ethernet/motorcomm/yt6801/yt6801_net.c    | 242 ++++++++++++++++++
 1 file changed, 242 insertions(+)

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
index 39b91cc26..eedf4dcb0 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
@@ -11,6 +11,8 @@
 #include "yt6801_desc.h"
 #include "yt6801_net.h"
 
+static void fxgmac_napi_enable(struct fxgmac_pdata *pdata);
+
 static int fxgmac_calc_rx_buf_size(struct fxgmac_pdata *pdata, unsigned int mtu)
 {
 	u32 rx_buf_size, max_mtu;
@@ -31,6 +33,26 @@ static int fxgmac_calc_rx_buf_size(struct fxgmac_pdata *pdata, unsigned int mtu)
 	return rx_buf_size;
 }
 
+static void fxgmac_enable_rx_tx_ints(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+	enum fxgmac_int int_id;
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		if (channel->tx_ring && channel->rx_ring)
+			int_id = FXGMAC_INT_DMA_CH_SR_TI_RI;
+		else if (channel->tx_ring)
+			int_id = FXGMAC_INT_DMA_CH_SR_TI;
+		else if (channel->rx_ring)
+			int_id = FXGMAC_INT_DMA_CH_SR_RI;
+		else
+			continue;
+
+		hw_ops->enable_channel_irq(channel, int_id);
+	}
+}
+
 #define FXGMAC_NAPI_ENABLE			0x1
 #define FXGMAC_NAPI_DISABLE			0x0
 static void fxgmac_napi_disable(struct fxgmac_pdata *pdata)
@@ -180,6 +202,160 @@ void fxgmac_free_rx_data(struct fxgmac_pdata *pdata)
 	}
 }
 
+static  void fxgmac_phylink_handler(struct net_device *ndev)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(ndev);
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+
+	pdata->phy_link = pdata->phydev->link;
+	pdata->phy_speed = pdata->phydev->speed;
+	pdata->phy_duplex = pdata->phydev->duplex;
+
+	yt_dbg(pdata, "EPHY_CTRL:%x, link:%d, speed:%d,  duplex:%x.\n",
+	       rd32_mem(pdata, EPHY_CTRL), pdata->phy_link, pdata->phy_speed,
+	       pdata->phy_duplex);
+
+	if (pdata->phy_link) {
+		hw_ops->config_mac_speed(pdata);
+		hw_ops->enable_rx(pdata);
+		hw_ops->enable_tx(pdata);
+		netif_carrier_on(pdata->netdev);
+		if (netif_running(pdata->netdev)) {
+			netif_tx_wake_all_queues(pdata->netdev);
+			yt_dbg(pdata, "%s now is link up, mac_speed=%d.\n",
+			       netdev_name(pdata->netdev), pdata->phy_speed);
+		}
+	} else {
+		netif_carrier_off(pdata->netdev);
+		netif_tx_stop_all_queues(pdata->netdev);
+		hw_ops->disable_rx(pdata);
+		hw_ops->disable_tx(pdata);
+		yt_dbg(pdata, "%s now is link down\n",
+		       netdev_name(pdata->netdev));
+	}
+
+	phy_print_status(pdata->phydev);
+}
+
+static int fxgmac_phy_connect(struct fxgmac_pdata *pdata)
+{
+	struct phy_device *phydev = pdata->phydev;
+	int ret;
+
+	ret = phy_connect_direct(pdata->netdev, phydev, fxgmac_phylink_handler,
+				 PHY_INTERFACE_MODE_RGMII);
+	if (ret)
+		return ret;
+
+	phy_attached_info(phydev);
+	return 0;
+}
+
+static void fxgmac_enable_msix_irqs(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+
+	for (u32 intid = 0; intid < MSIX_TBL_MAX_NUM; intid++)
+		hw_ops->enable_msix_one_irq(pdata, intid);
+}
+
+int fxgmac_phy_irq_enable(struct fxgmac_pdata *pdata, bool clear_phy_interrupt)
+{
+	struct phy_device *phydev = pdata->phydev;
+
+	if (clear_phy_interrupt &&
+	    phy_read(phydev, PHY_INT_STATUS) < 0)
+		return -ETIMEDOUT;
+
+	return phy_modify(phydev, PHY_INT_MASK,
+				     PHY_INT_MASK_LINK_UP |
+					     PHY_INT_MASK_LINK_DOWN,
+				     PHY_INT_MASK_LINK_UP |
+					     PHY_INT_MASK_LINK_DOWN);
+}
+
+int fxgmac_start(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+	u32 val;
+	int ret;
+
+	if (pdata->dev_state != FXGMAC_DEV_OPEN &&
+	    pdata->dev_state != FXGMAC_DEV_STOP &&
+	    pdata->dev_state != FXGMAC_DEV_RESUME) {
+		yt_dbg(pdata, " dev_state err:%x\n", pdata->dev_state);
+		return 0;
+	}
+
+	if (pdata->dev_state != FXGMAC_DEV_STOP) {
+		hw_ops->reset_phy(pdata);
+		hw_ops->release_phy(pdata);
+		yt_dbg(pdata, "reset phy.\n");
+	}
+
+	if (pdata->dev_state == FXGMAC_DEV_OPEN) {
+		ret = fxgmac_phy_connect(pdata);
+		if (ret < 0)
+			return ret;
+
+		yt_dbg(pdata, "fxgmac_phy_connect.\n");
+	}
+
+	phy_init_hw(pdata->phydev);
+	phy_resume(pdata->phydev);
+
+	hw_ops->pcie_init(pdata);
+	if (test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->powerstate)) {
+		yt_err(pdata,
+		       "fxgmac powerstate is %lu when config power up.\n",
+		       pdata->powerstate);
+	}
+
+	hw_ops->config_power_up(pdata);
+	hw_ops->dismiss_all_int(pdata);
+	ret = hw_ops->init(pdata);
+	if (ret < 0) {
+		yt_err(pdata, "fxgmac hw init error.\n");
+		return ret;
+	}
+
+	fxgmac_napi_enable(pdata);
+	ret = fxgmac_request_irqs(pdata);
+	if (ret < 0)
+		return ret;
+
+	/* Config interrupt to level signal */
+	val = rd32_mac(pdata, DMA_MR);
+	fxgmac_set_bits(&val, DMA_MR_INTM_POS, DMA_MR_INTM_LEN, 2);
+	fxgmac_set_bits(&val, DMA_MR_QUREAD_POS, DMA_MR_QUREAD_LEN, 1);
+	wr32_mac(pdata, val, DMA_MR);
+
+	hw_ops->enable_mgm_irq(pdata);
+	hw_ops->set_interrupt_moderation(pdata);
+
+	if (pdata->per_channel_irq) {
+		fxgmac_enable_msix_irqs(pdata);
+		ret = fxgmac_phy_irq_enable(pdata, true);
+		if (ret < 0)
+			goto dis_napi;
+	}
+
+	fxgmac_enable_rx_tx_ints(pdata);
+	phy_speed_up(pdata->phydev);
+	genphy_soft_reset(pdata->phydev);
+
+	pdata->dev_state = FXGMAC_DEV_START;
+	phy_start(pdata->phydev);
+
+	return 0;
+
+dis_napi:
+	fxgmac_napi_disable(pdata);
+	hw_ops->exit(pdata);
+	yt_err(pdata, "%s irq err.\n", __func__);
+	return ret;
+}
+
 static void fxgmac_disable_msix_irqs(struct fxgmac_pdata *pdata)
 {
 	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
@@ -540,4 +716,70 @@ static const struct net_device_ops fxgmac_netdev_ops = {
 const struct net_device_ops *fxgmac_get_netdev_ops(void)
 {
 	return &fxgmac_netdev_ops;
+
+static void fxgmac_napi_enable(struct fxgmac_pdata *pdata)
+{
+	u32 i, *flags = &pdata->int_flags;
+	struct fxgmac_channel *channel;
+	u32 misc_napi, tx, rx;
+
+	misc_napi = FIELD_GET(BIT(FXGMAC_FLAG_MISC_NAPI_POS), *flags);
+	tx = FXGMAC_GET_BITS(*flags, FXGMAC_FLAG_TX_NAPI_POS,
+			     FXGMAC_FLAG_TX_NAPI_LEN);
+	rx = FXGMAC_GET_BITS(*flags, FXGMAC_FLAG_RX_NAPI_POS,
+			     FXGMAC_FLAG_RX_NAPI_LEN);
+
+	if (!pdata->per_channel_irq) {
+		i = FIELD_GET(BIT(FXGMAC_FLAG_LEGACY_NAPI_POS), *flags);
+		if (i)
+			return;
+
+		netif_napi_add_weight(pdata->netdev, &pdata->napi,
+				      fxgmac_all_poll,
+				      NAPI_POLL_WEIGHT);
+
+		napi_enable(&pdata->napi);
+		fxgmac_set_bits(flags, FXGMAC_FLAG_LEGACY_NAPI_POS,
+				FXGMAC_FLAG_LEGACY_NAPI_LEN,
+				FXGMAC_NAPI_ENABLE);
+		return;
+	}
+
+	channel = pdata->channel_head;
+
+	for (i = 0; i < pdata->channel_count; i++, channel++) {
+		if (!FXGMAC_GET_BITS(rx, i, FXGMAC_FLAG_PER_RX_NAPI_LEN)) {
+			netif_napi_add_weight(pdata->netdev,
+					      &channel->napi_rx,
+					      fxgmac_one_poll_rx,
+					      NAPI_POLL_WEIGHT);
+
+			napi_enable(&channel->napi_rx);
+			fxgmac_set_bits(flags, FXGMAC_FLAG_RX_NAPI_POS + i,
+					FXGMAC_FLAG_PER_RX_NAPI_LEN,
+					FXGMAC_NAPI_ENABLE);
+		}
+
+		if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i) && !tx) {
+			netif_napi_add_weight(pdata->netdev, &channel->napi_tx,
+					      fxgmac_one_poll_tx,
+					      NAPI_POLL_WEIGHT);
+			napi_enable(&channel->napi_tx);
+			fxgmac_set_bits(flags, FXGMAC_FLAG_TX_NAPI_POS,
+					FXGMAC_FLAG_TX_NAPI_LEN,
+					FXGMAC_NAPI_ENABLE);
+		}
+		if (netif_msg_drv(pdata))
+			yt_dbg(pdata, "msix ch%d napi enabled done.\n", i);
+	}
+
+	/* Misc */
+	if (!misc_napi) {
+		netif_napi_add_weight(pdata->netdev, &pdata->napi_misc,
+				      fxgmac_misc_poll, NAPI_POLL_WEIGHT);
+
+		napi_enable(&pdata->napi_misc);
+		fxgmac_set_bits(flags, FXGMAC_FLAG_MISC_NAPI_POS,
+				FXGMAC_FLAG_MISC_NAPI_LEN, FXGMAC_NAPI_ENABLE);
+	}
 }
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 08/21] motorcomm:yt6801: Implement the fxgmac_read_mac_addr function
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (2 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 05/21] motorcomm:yt6801: Implement the fxgmac_start function Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 12/21] motorcomm:yt6801: Implement .ndo_tx_timeout and .ndo_change_mtu functions Frank Sae
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement the fxgmac_read_mac_addr function to read mac address form efuse.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../ethernet/motorcomm/yt6801/yt6801_net.c    | 147 ++++++++++++++++++
 1 file changed, 147 insertions(+)

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
index aa51ecdd7..c3baa06b0 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
@@ -796,6 +796,153 @@ static int fxgmac_open(struct net_device *netdev)
 	return ret;
 }
 
+#define EFUSE_FISRT_UPDATE_ADDR				255
+#define EFUSE_SECOND_UPDATE_ADDR			209
+#define EFUSE_MAX_ENTRY					39
+#define EFUSE_PATCH_ADDR_START				0
+#define EFUSE_PATCH_DATA_START				2
+#define EFUSE_PATCH_SIZE				6
+#define EFUSE_REGION_A_B_LENGTH				18
+
+static bool fxgmac_efuse_read_data(struct fxgmac_pdata *pdata, u32 offset,
+				   u8 *value)
+{
+	bool ret = false;
+	u32 wait = 1000;
+	u32 val = 0;
+
+	fxgmac_set_bits(&val, EFUSE_OP_ADDR_POS, EFUSE_OP_ADDR_LEN, offset);
+	fxgmac_set_bits(&val, EFUSE_OP_START_POS, EFUSE_OP_START_LEN, 1);
+	fxgmac_set_bits(&val, EFUSE_OP_MODE_POS, EFUSE_OP_MODE_LEN,
+			EFUSE_OP_MODE_ROW_READ);
+	wr32_mem(pdata, val, EFUSE_OP_CTRL_0);
+
+	while (wait--) {
+		fsleep(20);
+		val = rd32_mem(pdata, EFUSE_OP_CTRL_1);
+		if (FXGMAC_GET_BITS(val, EFUSE_OP_DONE_POS,
+				    EFUSE_OP_DONE_LEN)) {
+			ret = true;
+			break;
+		}
+	}
+
+	if (!ret) {
+		yt_err(pdata, "Fail to reading efuse Byte%d\n", offset);
+		return ret;
+	}
+
+	if (value)
+		*value = FXGMAC_GET_BITS(val, EFUSE_OP_RD_DATA_POS,
+					 EFUSE_OP_RD_DATA_LEN) & 0xff;
+
+	return ret;
+}
+
+static bool fxgmac_efuse_read_index_patch(struct fxgmac_pdata *pdata, u8 index,
+					  u32 *offset, u32 *value)
+{
+	u8 tmp[EFUSE_PATCH_SIZE - EFUSE_PATCH_DATA_START];
+	u32 addr, i;
+	bool ret;
+
+	if (index >= EFUSE_MAX_ENTRY) {
+		yt_err(pdata, "Reading efuse out of range, index %d\n", index);
+		return false;
+	}
+
+	for (i = EFUSE_PATCH_ADDR_START; i < EFUSE_PATCH_DATA_START; i++) {
+		addr = EFUSE_REGION_A_B_LENGTH + index * EFUSE_PATCH_SIZE + i;
+		ret = fxgmac_efuse_read_data(pdata, addr,
+					     tmp + i - EFUSE_PATCH_ADDR_START);
+		if (!ret) {
+			yt_err(pdata, "Fail to reading efuse Byte%d\n", addr);
+			return ret;
+		}
+	}
+	if (offset) {
+		/* tmp[0] is low 8bit date, tmp[1] is high 8bit date */
+		*offset = tmp[0] | (tmp[1] << 8);
+	}
+
+	for (i = EFUSE_PATCH_DATA_START; i < EFUSE_PATCH_SIZE; i++) {
+		addr = EFUSE_REGION_A_B_LENGTH + index * EFUSE_PATCH_SIZE + i;
+		ret = fxgmac_efuse_read_data(pdata, addr,
+					     tmp + i - EFUSE_PATCH_DATA_START);
+		if (!ret) {
+			yt_err(pdata, "Fail to reading efuse Byte%d\n", addr);
+			return ret;
+		}
+	}
+	if (value) {
+		/* tmp[0] is low 8bit date, tmp[1] is low 8bit date
+		 * ...  tmp[3] is highest 8bit date
+		 */
+		*value = tmp[0] | (tmp[1] << 8) | (tmp[2] << 16) |
+			 (tmp[3] << 24);
+	}
+
+	return ret;
+}
+
+static bool fxgmac_efuse_read_mac_subsys(struct fxgmac_pdata *pdata,
+					 u8 *mac_addr, u32 *subsys, u32 *revid)
+{
+	u32 machr = 0, maclr = 0;
+	u32 offset = 0, val = 0;
+	bool ret = true;
+	u8 index;
+
+	for (index = 0;; index++) {
+		if (!fxgmac_efuse_read_index_patch(pdata, index, &offset, &val))
+			return false;
+
+		if (offset == 0x00)
+			break; /* Reach the blank. */
+
+		if (offset == MACA0LR_FROM_EFUSE)
+			maclr = val;
+
+		if (offset == MACA0HR_FROM_EFUSE)
+			machr = val;
+
+		if (offset == PCI_REVISION_ID && revid)
+			*revid = val;
+
+		if (offset == PCI_SUBSYSTEM_VENDOR_ID && subsys)
+			*subsys = val;
+	}
+
+	if (mac_addr) {
+		mac_addr[5] = (u8)(maclr & 0xFF);
+		mac_addr[4] = (u8)((maclr >> 8) & 0xFF);
+		mac_addr[3] = (u8)((maclr >> 16) & 0xFF);
+		mac_addr[2] = (u8)((maclr >> 24) & 0xFF);
+		mac_addr[1] = (u8)(machr & 0xFF);
+		mac_addr[0] = (u8)((machr >> 8) & 0xFF);
+	}
+
+	return ret;
+}
+
+static int fxgmac_read_mac_addr(struct fxgmac_pdata *pdata)
+{
+	u8 default_addr[ETH_ALEN] = { 0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7 };
+	struct net_device *netdev = pdata->netdev;
+	int ret;
+
+	/* If efuse have mac addr, use it. if not, use static mac address. */
+	ret = fxgmac_efuse_read_mac_subsys(pdata, pdata->mac_addr, NULL, NULL);
+	if (!ret)
+		return -1;
+
+	if (is_zero_ether_addr(pdata->mac_addr))
+		/* Use a static mac address for test */
+		memcpy(pdata->mac_addr, default_addr, netdev->addr_len);
+
+	return 0;
+}
+
 #define FXGMAC_SYSCLOCK 125000000 /* System clock is 125 MHz */
 
 static void fxgmac_default_config(struct fxgmac_pdata *pdata)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 12/21] motorcomm:yt6801: Implement .ndo_tx_timeout and .ndo_change_mtu functions
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (3 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 08/21] motorcomm:yt6801: Implement the fxgmac_read_mac_addr function Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 13/21] motorcomm:yt6801: Implement some ethtool_ops function Frank Sae
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement following callback function
.ndo_tx_timeout
.ndo_change_mtu
The .ndo_tx_timeout function also will call fxgmac_dump_state to dump nessary
debug information.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../net/ethernet/motorcomm/yt6801/yt6801_hw.c |  69 ++++++++
 .../ethernet/motorcomm/yt6801/yt6801_net.c    | 160 ++++++++++++++++++
 2 files changed, 229 insertions(+)

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
index 791dd69b7..a70fa4ede 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
@@ -1204,6 +1204,71 @@ static void fxgmac_rx_mmc_int(struct fxgmac_pdata *yt)
 		stats->rxcontrolframe_g += rd32_mac(yt, MMC_RXCONTROLFRAME_G);
 }
 
+static void fxgmac_read_mmc_stats(struct fxgmac_pdata *yt)
+{
+	struct fxgmac_stats *stats = &yt->stats;
+
+	stats->txoctetcount_gb += rd32_mac(yt, MMC_TXOCTETCOUNT_GB_LO);
+	stats->txframecount_gb += rd32_mac(yt, MMC_TXFRAMECOUNT_GB_LO);
+	stats->txbroadcastframes_g += rd32_mac(yt, MMC_TXBROADCASTFRAMES_G_LO);
+	stats->txmulticastframes_g += rd32_mac(yt, MMC_TXMULTICASTFRAMES_G_LO);
+	stats->tx64octets_gb += rd32_mac(yt, MMC_TX64OCTETS_GB_LO);
+	stats->tx65to127octets_gb += rd32_mac(yt, MMC_TX65TO127OCTETS_GB_LO);
+	stats->tx128to255octets_gb += rd32_mac(yt, MMC_TX128TO255OCTETS_GB_LO);
+	stats->tx256to511octets_gb += rd32_mac(yt, MMC_TX256TO511OCTETS_GB_LO);
+	stats->tx512to1023octets_gb +=
+		rd32_mac(yt, MMC_TX512TO1023OCTETS_GB_LO);
+	stats->tx1024tomaxoctets_gb +=
+		rd32_mac(yt, MMC_TX1024TOMAXOCTETS_GB_LO);
+	stats->txunicastframes_gb += rd32_mac(yt, MMC_TXUNICASTFRAMES_GB_LO);
+	stats->txmulticastframes_gb +=
+		rd32_mac(yt, MMC_TXMULTICASTFRAMES_GB_LO);
+	stats->txbroadcastframes_g += rd32_mac(yt, MMC_TXBROADCASTFRAMES_GB_LO);
+	stats->txunderflowerror += rd32_mac(yt, MMC_TXUNDERFLOWERROR_LO);
+	stats->txsinglecollision_g += rd32_mac(yt, MMC_TXSINGLECOLLISION_G);
+	stats->txmultiplecollision_g += rd32_mac(yt, MMC_TXMULTIPLECOLLISION_G);
+	stats->txdeferredframes += rd32_mac(yt, MMC_TXDEFERREDFRAMES);
+	stats->txlatecollisionframes += rd32_mac(yt, MMC_TXLATECOLLISIONFRAMES);
+	stats->txexcessivecollisionframes +=
+		rd32_mac(yt, MMC_TXEXCESSIVECOLLSIONFRAMES);
+	stats->txcarriererrorframes += rd32_mac(yt, MMC_TXCARRIERERRORFRAMES);
+	stats->txoctetcount_g += rd32_mac(yt, MMC_TXOCTETCOUNT_G_LO);
+	stats->txframecount_g += rd32_mac(yt, MMC_TXFRAMECOUNT_G_LO);
+	stats->txexcessivedeferralerror +=
+		rd32_mac(yt, MMC_TXEXCESSIVEDEFERRALERROR);
+	stats->txpauseframes += rd32_mac(yt, MMC_TXPAUSEFRAMES_LO);
+	stats->txvlanframes_g += rd32_mac(yt, MMC_TXVLANFRAMES_G_LO);
+	stats->txoversize_g += rd32_mac(yt, MMC_TXOVERSIZEFRAMES);
+	stats->rxframecount_gb += rd32_mac(yt, MMC_RXFRAMECOUNT_GB_LO);
+	stats->rxoctetcount_gb += rd32_mac(yt, MMC_RXOCTETCOUNT_GB_LO);
+	stats->rxoctetcount_g += rd32_mac(yt, MMC_RXOCTETCOUNT_G_LO);
+	stats->rxbroadcastframes_g += rd32_mac(yt, MMC_RXBROADCASTFRAMES_G_LO);
+	stats->rxmulticastframes_g += rd32_mac(yt, MMC_RXMULTICASTFRAMES_G_LO);
+	stats->rxcrcerror += rd32_mac(yt, MMC_RXCRCERROR_LO);
+	stats->rxalignerror += rd32_mac(yt, MMC_RXALIGNERROR);
+	stats->rxrunterror += rd32_mac(yt, MMC_RXRUNTERROR);
+	stats->rxjabbererror += rd32_mac(yt, MMC_RXJABBERERROR);
+	stats->rxundersize_g += rd32_mac(yt, MMC_RXUNDERSIZE_G);
+	stats->rxoversize_g += rd32_mac(yt, MMC_RXOVERSIZE_G);
+	stats->rx64octets_gb += rd32_mac(yt, MMC_RX64OCTETS_GB_LO);
+	stats->rx65to127octets_gb += rd32_mac(yt, MMC_RX65TO127OCTETS_GB_LO);
+	stats->rx128to255octets_gb += rd32_mac(yt, MMC_RX128TO255OCTETS_GB_LO);
+	stats->rx256to511octets_gb += rd32_mac(yt, MMC_RX256TO511OCTETS_GB_LO);
+	stats->rx512to1023octets_gb +=
+		rd32_mac(yt, MMC_RX512TO1023OCTETS_GB_LO);
+	stats->rx1024tomaxoctets_gb +=
+		rd32_mac(yt, MMC_RX1024TOMAXOCTETS_GB_LO);
+	stats->rxunicastframes_g += rd32_mac(yt, MMC_RXUNICASTFRAMES_G_LO);
+	stats->rxlengtherror += rd32_mac(yt, MMC_RXLENGTHERROR_LO);
+	stats->rxoutofrangetype += rd32_mac(yt, MMC_RXOUTOFRANGETYPE_LO);
+	stats->rxpauseframes += rd32_mac(yt, MMC_RXPAUSEFRAMES_LO);
+	stats->rxfifooverflow += rd32_mac(yt, MMC_RXFIFOOVERFLOW_LO);
+	stats->rxvlanframes_gb += rd32_mac(yt, MMC_RXVLANFRAMES_GB_LO);
+	stats->rxwatchdogerror += rd32_mac(yt, MMC_RXWATCHDOGERROR);
+	stats->rxreceiveerrorframe += rd32_mac(yt, MMC_RXRECEIVEERRORFRAME);
+	stats->rxcontrolframe_g += rd32_mac(yt, MMC_RXCONTROLFRAME_G);
+}
+
 static void fxgmac_config_mmc(struct fxgmac_pdata *pdata)
 {
 	u32 val;
@@ -2610,6 +2675,10 @@ void fxgmac_hw_ops_init(struct fxgmac_hw_ops *hw_ops)
 	/* RX coalescing */
 	hw_ops->config_rx_coalesce = fxgmac_config_rx_coalesce;
 	hw_ops->usec_to_riwt = fxgmac_usec_to_riwt;
+
+	/* MMC statistics support */
+	hw_ops->read_mmc_stats = fxgmac_read_mmc_stats;
+
 	/* Receive Side Scaling */
 	hw_ops->enable_rss = fxgmac_enable_rss;
 	hw_ops->disable_rss = fxgmac_disable_rss;
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
index ed65c9cc9..c5c13601e 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
@@ -1015,6 +1015,129 @@ static int fxgmac_close(struct net_device *netdev)
 	return 0;
 }
 
+static void fxgmac_dump_state(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	struct fxgmac_stats *pstats = &pdata->stats;
+	struct fxgmac_ring *ring;
+
+	ring = &channel->tx_ring[0];
+	yt_err(pdata, "Tx descriptor info:\n");
+	yt_err(pdata, "Tx cur = 0x%x\n", ring->cur);
+	yt_err(pdata, "Tx dirty = 0x%x\n", ring->dirty);
+	yt_err(pdata, "Tx dma_desc_head = %pad\n", &ring->dma_desc_head);
+	yt_err(pdata, "Tx desc_data_head = %pad\n", &ring->desc_data_head);
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		ring = &channel->rx_ring[0];
+		yt_err(pdata, "Rx[%d] descriptor info:\n", i);
+		yt_err(pdata, "Rx cur = 0x%x\n", ring->cur);
+		yt_err(pdata, "Rx dirty = 0x%x\n", ring->dirty);
+		yt_err(pdata, "Rx dma_desc_head = %pad\n",
+		       &ring->dma_desc_head);
+		yt_err(pdata, "Rx desc_data_head = %pad\n",
+		       &ring->desc_data_head);
+	}
+
+	yt_err(pdata, "Device Registers:\n");
+	yt_err(pdata, "MAC_ISR = %08x\n", rd32_mac(pdata, MAC_ISR));
+	yt_err(pdata, "MAC_IER = %08x\n", rd32_mac(pdata, MAC_IER));
+	yt_err(pdata, "MMC_RISR = %08x\n", rd32_mac(pdata, MMC_RISR));
+	yt_err(pdata, "MMC_RIER = %08x\n", rd32_mac(pdata, MMC_RIER));
+	yt_err(pdata, "MMC_TISR = %08x\n", rd32_mac(pdata, MMC_TISR));
+	yt_err(pdata, "MMC_TIER = %08x\n", rd32_mac(pdata, MMC_TIER));
+
+	yt_err(pdata, "EPHY_CTRL = %04x\n", rd32_mem(pdata, EPHY_CTRL));
+	yt_err(pdata, "MGMT_INT_CTRL0 = %04x\n",
+	       rd32_mem(pdata, MGMT_INT_CTRL0));
+	yt_err(pdata, "LPW_CTRL = %04x\n", rd32_mem(pdata, LPW_CTRL));
+	yt_err(pdata, "MSIX_TBL_MASK = %04x\n", rd32_mem(pdata, MSIX_TBL_MASK));
+
+	yt_err(pdata, "Dump nonstick regs:\n");
+	for (u32 i = GLOBAL_CTRL0; i < MSI_PBA; i += 4)
+		yt_err(pdata, "[%d] = %04x\n", i / 4, rd32_mem(pdata, i));
+
+	pdata->hw_ops.read_mmc_stats(pdata);
+
+	yt_err(pdata, "Dump TX counters:\n");
+	yt_err(pdata, "tx_packets %lld\n", pstats->txframecount_gb);
+	yt_err(pdata, "tx_errors %lld\n",
+	       pstats->txframecount_gb - pstats->txframecount_g);
+	yt_err(pdata, "tx_multicastframes_errors %lld\n",
+	       pstats->txmulticastframes_gb - pstats->txmulticastframes_g);
+	yt_err(pdata, "tx_broadcastframes_errors %lld\n",
+	       pstats->txbroadcastframes_gb - pstats->txbroadcastframes_g);
+
+	yt_err(pdata, "txunderflowerror %lld\n", pstats->txunderflowerror);
+	yt_err(pdata, "txdeferredframes %lld\n",
+	       pstats->txdeferredframes);
+	yt_err(pdata, "txlatecollisionframes %lld\n",
+	       pstats->txlatecollisionframes);
+	yt_err(pdata, "txexcessivecollisionframes %lld\n",
+	       pstats->txexcessivecollisionframes);
+	yt_err(pdata, "txcarriererrorframes %lld\n",
+	       pstats->txcarriererrorframes);
+	yt_err(pdata, "txexcessivedeferralerror %lld\n",
+	       pstats->txexcessivedeferralerror);
+
+	yt_err(pdata, "txsinglecollision_g %lld\n",
+	       pstats->txsinglecollision_g);
+	yt_err(pdata, "txmultiplecollision_g %lld\n",
+	       pstats->txmultiplecollision_g);
+	yt_err(pdata, "txoversize_g %lld\n", pstats->txoversize_g);
+
+	yt_err(pdata, "Dump RX counters:\n");
+	yt_err(pdata, "rx_packets %lld\n", pstats->rxframecount_gb);
+	yt_err(pdata, "rx_errors %lld\n",
+	       pstats->rxframecount_gb - pstats->rxbroadcastframes_g -
+	       pstats->rxmulticastframes_g - pstats->rxunicastframes_g);
+
+	yt_err(pdata, "rx_crc_errors %lld\n", pstats->rxcrcerror);
+	yt_err(pdata, "rxalignerror %lld\n", pstats->rxalignerror);
+	yt_err(pdata, "rxrunterror %lld\n", pstats->rxrunterror);
+	yt_err(pdata, "rxjabbererror %lld\n", pstats->rxjabbererror);
+	yt_err(pdata, "rx_length_errors %lld\n", pstats->rxlengtherror);
+	yt_err(pdata, "rxoutofrangetype %lld\n", pstats->rxoutofrangetype);
+	yt_err(pdata, "rx_fifo_errors %lld\n", pstats->rxfifooverflow);
+	yt_err(pdata, "rxwatchdogerror %lld\n", pstats->rxwatchdogerror);
+	yt_err(pdata, "rxreceiveerrorframe %lld\n",
+	       pstats->rxreceiveerrorframe);
+
+	yt_err(pdata, "rxbroadcastframes_g %lld\n",
+	       pstats->rxbroadcastframes_g);
+	yt_err(pdata, "rxmulticastframes_g %lld\n",
+	       pstats->rxmulticastframes_g);
+	yt_err(pdata, "rxundersize_g %lld\n", pstats->rxundersize_g);
+	yt_err(pdata, "rxoversize_g %lld\n", pstats->rxoversize_g);
+	yt_err(pdata, "rxunicastframes_g %lld\n", pstats->rxunicastframes_g);
+	yt_err(pdata, "rxcontrolframe_g %lld\n", pstats->rxcontrolframe_g);
+
+	yt_err(pdata, "Dump Extra counters:\n");
+	yt_err(pdata, "tx_tso_packets %lld\n", pstats->tx_tso_packets);
+	yt_err(pdata, "rx_split_header_packets %lld\n",
+	       pstats->rx_split_header_packets);
+	yt_err(pdata, "tx_process_stopped %lld\n", pstats->tx_process_stopped);
+	yt_err(pdata, "rx_process_stopped %lld\n", pstats->rx_process_stopped);
+	yt_err(pdata, "tx_buffer_unavailable %lld\n",
+	       pstats->tx_buffer_unavailable);
+	yt_err(pdata, "rx_buffer_unavailable %lld\n",
+	       pstats->rx_buffer_unavailable);
+	yt_err(pdata, "fatal_bus_error %lld\n", pstats->fatal_bus_error);
+	yt_err(pdata, "napi_poll_isr %lld\n", pstats->napi_poll_isr);
+	yt_err(pdata, "napi_poll_txtimer %lld\n", pstats->napi_poll_txtimer);
+	yt_err(pdata, "ephy_poll_timer_cnt %lld\n",
+	       pstats->ephy_poll_timer_cnt);
+	yt_err(pdata, "mgmt_int_isr %lld\n", pstats->mgmt_int_isr);
+}
+
+static void fxgmac_tx_timeout(struct net_device *netdev, unsigned int unused)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+
+	fxgmac_dump_state(pdata);
+	schedule_work(&pdata->restart_work);
+}
+
 #define EFUSE_FISRT_UPDATE_ADDR				255
 #define EFUSE_SECOND_UPDATE_ADDR			209
 #define EFUSE_MAX_ENTRY					39
@@ -2046,6 +2169,41 @@ static int fxgmac_set_mac_address(struct net_device *netdev, void *addr)
 	return 0;
 }
 
+static int fxgmac_change_mtu(struct net_device *netdev, int mtu)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	int old_mtu = netdev->mtu;
+	int ret;
+
+	mutex_lock(&pdata->mutex);
+	fxgmac_stop(pdata);
+	fxgmac_free_tx_data(pdata);
+
+	/* We must unmap rx desc's dma before we change rx_buf_size.
+	 * Becaues the size of the unmapped DMA is set according to rx_buf_size
+	 */
+	fxgmac_free_rx_data(pdata);
+	pdata->jumbo = mtu > ETH_DATA_LEN ? 1 : 0;
+	ret = fxgmac_calc_rx_buf_size(pdata, mtu);
+	if (ret < 0)
+		return ret;
+
+	pdata->rx_buf_size = ret;
+	netdev->mtu = mtu;
+
+	if (netif_running(netdev))
+		fxgmac_start(pdata);
+
+	netdev_update_features(netdev);
+
+	mutex_unlock(&pdata->mutex);
+
+	yt_dbg(pdata, "fxgmac,set MTU from %d to %d. min, max=(%d,%d)\n",
+	       old_mtu, netdev->mtu, netdev->min_mtu, netdev->max_mtu);
+
+	return 0;
+}
+
 static int fxgmac_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
 				  u16 vid)
 {
@@ -2169,7 +2327,9 @@ static const struct net_device_ops fxgmac_netdev_ops = {
 	.ndo_open		= fxgmac_open,
 	.ndo_stop		= fxgmac_close,
 	.ndo_start_xmit		= fxgmac_xmit,
+	.ndo_tx_timeout		= fxgmac_tx_timeout,
 	.ndo_get_stats64	= fxgmac_get_stats64,
+	.ndo_change_mtu		= fxgmac_change_mtu,
 	.ndo_set_mac_address	= fxgmac_set_mac_address,
 	.ndo_validate_addr	= eth_validate_addr,
 	.ndo_vlan_rx_add_vid	= fxgmac_vlan_rx_add_vid,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 13/21] motorcomm:yt6801: Implement some ethtool_ops function
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (4 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 12/21] motorcomm:yt6801: Implement .ndo_tx_timeout and .ndo_change_mtu functions Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 14/21] motorcomm:yt6801: Implement the WOL function of ethtool_ops Frank Sae
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement following callback function
.get_drvinfo
.get_link
.get_msglevel
.set_msglevel
.get_channels
.get_coalesce
.set_coalesce
.supported_coalesce_params
.get_strings
.get_sset_count
.get_ethtool_stats
.get_regs_len
.get_regs
.get_ringparam
.set_ringparam
.get_rxnfc
.set_rxnfc
.get_rxfh_indir_size
.get_rxfh_key_size
.get_rxfh
.set_rxfh
.nway_reset
.reset
.get_link_ksettings
.set_link_ksettings
.get_pauseparam
.set_pauseparam

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../motorcomm/yt6801/yt6801_ethtool.c         | 738 ++++++++++++++++++
 1 file changed, 738 insertions(+)
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c
new file mode 100644
index 000000000..7989ccbc3
--- /dev/null
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c
@@ -0,0 +1,738 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2022 - 2024 Motorcomm Electronic Technology Co.,Ltd. */
+
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/kernel.h>
+
+#include "yt6801_desc.h"
+#include "yt6801_net.h"
+
+struct fxgmac_stats_desc {
+	char stat_string[ETH_GSTRING_LEN];
+	int stat_offset;
+};
+
+#define FXGMAC_STAT(str, var)                                                  \
+	{                                                                      \
+		str, offsetof(struct fxgmac_pdata, stats.var),                 \
+	}
+
+static const struct fxgmac_stats_desc fxgmac_gstring_stats[] = {
+	/* MMC TX counters */
+	FXGMAC_STAT("tx_bytes", txoctetcount_gb),
+	FXGMAC_STAT("tx_bytes_good", txoctetcount_g),
+	FXGMAC_STAT("tx_packets", txframecount_gb),
+	FXGMAC_STAT("tx_packets_good", txframecount_g),
+	FXGMAC_STAT("tx_unicast_packets", txunicastframes_gb),
+	FXGMAC_STAT("tx_broadcast_packets", txbroadcastframes_gb),
+	FXGMAC_STAT("tx_broadcast_packets_good", txbroadcastframes_g),
+	FXGMAC_STAT("tx_multicast_packets", txmulticastframes_gb),
+	FXGMAC_STAT("tx_multicast_packets_good", txmulticastframes_g),
+	FXGMAC_STAT("tx_vlan_packets_good", txvlanframes_g),
+	FXGMAC_STAT("tx_64_byte_packets", tx64octets_gb),
+	FXGMAC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb),
+	FXGMAC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb),
+	FXGMAC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb),
+	FXGMAC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb),
+	FXGMAC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb),
+	FXGMAC_STAT("tx_underflow_errors", txunderflowerror),
+	FXGMAC_STAT("tx_pause_frames", txpauseframes),
+	FXGMAC_STAT("tx_single_collision", txsinglecollision_g),
+	FXGMAC_STAT("tx_multiple_collision", txmultiplecollision_g),
+	FXGMAC_STAT("tx_deferred_frames", txdeferredframes),
+	FXGMAC_STAT("tx_late_collision_frames", txlatecollisionframes),
+	FXGMAC_STAT("tx_excessive_collision_frames",
+		    txexcessivecollisionframes),
+	FXGMAC_STAT("tx_carrier_error_frames", txcarriererrorframes),
+	FXGMAC_STAT("tx_excessive_deferral_error", txexcessivedeferralerror),
+	FXGMAC_STAT("tx_oversize_frames_good", txoversize_g),
+
+	/* MMC RX counters */
+	FXGMAC_STAT("rx_bytes", rxoctetcount_gb),
+	FXGMAC_STAT("rx_bytes_good", rxoctetcount_g),
+	FXGMAC_STAT("rx_packets", rxframecount_gb),
+	FXGMAC_STAT("rx_unicast_packets_good", rxunicastframes_g),
+	FXGMAC_STAT("rx_broadcast_packets_good", rxbroadcastframes_g),
+	FXGMAC_STAT("rx_multicast_packets_good", rxmulticastframes_g),
+	FXGMAC_STAT("rx_vlan_packets_mac", rxvlanframes_gb),
+	FXGMAC_STAT("rx_64_byte_packets", rx64octets_gb),
+	FXGMAC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb),
+	FXGMAC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb),
+	FXGMAC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb),
+	FXGMAC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb),
+	FXGMAC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb),
+	FXGMAC_STAT("rx_undersize_packets_good", rxundersize_g),
+	FXGMAC_STAT("rx_oversize_packets_good", rxoversize_g),
+	FXGMAC_STAT("rx_crc_errors", rxcrcerror),
+	FXGMAC_STAT("rx_align_error", rxalignerror),
+	FXGMAC_STAT("rx_crc_errors_small_packets", rxrunterror),
+	FXGMAC_STAT("rx_crc_errors_giant_packets", rxjabbererror),
+	FXGMAC_STAT("rx_length_errors", rxlengtherror),
+	FXGMAC_STAT("rx_out_of_range_errors", rxoutofrangetype),
+	FXGMAC_STAT("rx_fifo_overflow_errors", rxfifooverflow),
+	FXGMAC_STAT("rx_watchdog_errors", rxwatchdogerror),
+	FXGMAC_STAT("rx_pause_frames", rxpauseframes),
+	FXGMAC_STAT("rx_receive_error_frames", rxreceiveerrorframe),
+	FXGMAC_STAT("rx_control_frames_good", rxcontrolframe_g),
+
+	/* Extra counters */
+	FXGMAC_STAT("tx_tso_packets", tx_tso_packets),
+	FXGMAC_STAT("rx_split_header_packets", rx_split_header_packets),
+	FXGMAC_STAT("tx_process_stopped", tx_process_stopped),
+	FXGMAC_STAT("rx_process_stopped", rx_process_stopped),
+	FXGMAC_STAT("tx_buffer_unavailable", tx_buffer_unavailable),
+	FXGMAC_STAT("rx_buffer_unavailable", rx_buffer_unavailable),
+	FXGMAC_STAT("fatal_bus_error", fatal_bus_error),
+	FXGMAC_STAT("tx_vlan_packets_net", tx_vlan_packets),
+	FXGMAC_STAT("rx_vlan_packets_net", rx_vlan_packets),
+	FXGMAC_STAT("napi_poll_isr", napi_poll_isr),
+	FXGMAC_STAT("napi_poll_txtimer", napi_poll_txtimer),
+	FXGMAC_STAT("alive_cnt_txtimer", cnt_alive_txtimer),
+	FXGMAC_STAT("ephy_poll_timer", ephy_poll_timer_cnt),
+	FXGMAC_STAT("mgmt_int_isr", mgmt_int_isr),
+};
+
+#define FXGMAC_STATS_COUNT ARRAY_SIZE(fxgmac_gstring_stats)
+
+static void fxgmac_ethtool_get_drvinfo(struct net_device *netdev,
+				       struct ethtool_drvinfo *drvinfo)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	u32 ver = pdata->hw_feat.version;
+	u32 sver, devid, userver;
+
+	strscpy(drvinfo->version, pdata->drv_ver, sizeof(drvinfo->version));
+
+	/* S|SVER: MAC Version
+	 * D|DEVID: Indicates the Device family
+	 * U|USERVER: User-defined Version
+	 */
+	sver = FXGMAC_GET_BITS(ver, MAC_VR_SVER_POS, MAC_VR_SVER_LEN);
+	devid = FXGMAC_GET_BITS(ver, MAC_VR_DEVID_POS, MAC_VR_DEVID_LEN);
+	userver = FXGMAC_GET_BITS(ver, MAC_VR_USERVER_POS, MAC_VR_USERVER_LEN);
+
+	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+		 "S.D.U: %x.%x.%x", sver, devid, userver);
+}
+
+static u32 fxgmac_ethtool_get_msglevel(struct net_device *netdev)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+
+	return pdata->msg_enable;
+}
+
+static void fxgmac_ethtool_set_msglevel(struct net_device *netdev, u32 msglevel)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+
+	yt_dbg(pdata, "set msglvl from %08x to %08x\n", pdata->msg_enable,
+	       msglevel);
+	pdata->msg_enable = msglevel;
+}
+
+static void fxgmac_ethtool_get_channels(struct net_device *netdev,
+					struct ethtool_channels *channel)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+
+	/* report maximum channels */
+	channel->max_rx = FXGMAC_MAX_DMA_RX_CHANNELS;
+	channel->max_tx = FXGMAC_MAX_DMA_TX_CHANNELS;
+	channel->max_other = 0;
+	channel->max_combined =
+		channel->max_rx + channel->max_tx + channel->max_other;
+
+	yt_dbg(pdata, "channels max rx:%d, tx:%d, other:%d, combined:%d\n",
+	       channel->max_rx, channel->max_tx, channel->max_other,
+	       channel->max_combined);
+
+	channel->rx_count = pdata->rx_q_count;
+	channel->tx_count = FXGMAC_TX_1_Q;
+	channel->other_count = 0;
+	/* record RSS queues */
+	channel->combined_count =
+		channel->rx_count + channel->tx_count + channel->other_count;
+
+	yt_dbg(pdata, "channels count rx:%d, tx:%d, other:%d, combined:%d\n",
+	       channel->rx_count, channel->tx_count, channel->other_count,
+	       channel->combined_count);
+}
+
+static int
+fxgmac_ethtool_get_coalesce(struct net_device *netdev,
+			    struct ethtool_coalesce *ec,
+			    struct kernel_ethtool_coalesce *kernel_coal,
+			    struct netlink_ext_ack *extack)
+
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+
+	memset(ec, 0, sizeof(struct ethtool_coalesce));
+	ec->rx_coalesce_usecs = pdata->rx_usecs;
+	ec->tx_coalesce_usecs = pdata->tx_usecs;
+
+	return 0;
+}
+
+#define FXGMAC_MAX_DMA_RIWT 0xff
+#define FXGMAC_MIN_DMA_RIWT 0x01
+
+static int
+fxgmac_ethtool_set_coalesce(struct net_device *netdev,
+			    struct ethtool_coalesce *ec,
+			    struct kernel_ethtool_coalesce *kernel_coal,
+			    struct netlink_ext_ack *extack)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+	unsigned int rx_frames, rx_riwt, rx_usecs;
+	unsigned int tx_frames;
+
+	/* Check for not supported parameters */
+	if (ec->rx_coalesce_usecs_irq || ec->rx_max_coalesced_frames_irq ||
+	    ec->tx_coalesce_usecs_high || ec->tx_max_coalesced_frames_irq ||
+	    ec->tx_coalesce_usecs_irq || ec->stats_block_coalesce_usecs ||
+	    ec->pkt_rate_low || ec->use_adaptive_rx_coalesce ||
+	    ec->use_adaptive_tx_coalesce || ec->rx_max_coalesced_frames_low ||
+	    ec->rx_coalesce_usecs_low || ec->tx_coalesce_usecs_low ||
+	    ec->tx_max_coalesced_frames_low || ec->pkt_rate_high ||
+	    ec->rx_coalesce_usecs_high || ec->rx_max_coalesced_frames_high ||
+	    ec->tx_max_coalesced_frames_high || ec->rate_sample_interval)
+		return -EOPNOTSUPP;
+
+	rx_usecs = ec->rx_coalesce_usecs;
+	rx_riwt = hw_ops->usec_to_riwt(pdata, rx_usecs);
+	rx_frames = ec->rx_max_coalesced_frames;
+	tx_frames = ec->tx_max_coalesced_frames;
+
+	if (rx_riwt > FXGMAC_MAX_DMA_RIWT || rx_riwt < FXGMAC_MIN_DMA_RIWT ||
+	    rx_frames > pdata->rx_desc_count)
+		return -EINVAL;
+
+	if (tx_frames > pdata->tx_desc_count)
+		return -EINVAL;
+
+	pdata->rx_usecs = rx_usecs;
+	pdata->rx_frames = rx_frames;
+	pdata->rx_riwt = rx_riwt;
+	hw_ops->config_rx_coalesce(pdata);
+
+	pdata->tx_frames = tx_frames;
+	pdata->tx_usecs = ec->tx_coalesce_usecs;
+	hw_ops->set_interrupt_moderation(pdata);
+
+	return 0;
+}
+
+static u32 fxgmac_get_rxfh_key_size(struct net_device *netdev)
+{
+	return FXGMAC_RSS_HASH_KEY_SIZE;
+}
+
+static u32 fxgmac_rss_indir_size(struct net_device *netdev)
+{
+	return FXGMAC_RSS_MAX_TABLE_SIZE;
+}
+
+static void fxgmac_get_reta(struct fxgmac_pdata *pdata, u32 *indir)
+{
+	u16 rss_m = FXGMAC_MAX_DMA_RX_CHANNELS - 1;
+
+	for (u32 i = 0; i < FXGMAC_RSS_MAX_TABLE_SIZE; i++)
+		indir[i] = pdata->rss_table[i] & rss_m;
+}
+
+static int fxgmac_get_rxfh(struct net_device *netdev,
+			   struct ethtool_rxfh_param *rxfh)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+
+	if (rxfh->hfunc) {
+		rxfh->hfunc = ETH_RSS_HASH_TOP;
+		yt_dbg(pdata, "%s, hash function\n", __func__);
+	}
+
+	if (rxfh->indir) {
+		fxgmac_get_reta(pdata, rxfh->indir);
+		yt_dbg(pdata, "%s, indirection tab\n", __func__);
+	}
+
+	if (rxfh->key) {
+		memcpy(rxfh->key, pdata->rss_key,
+		       fxgmac_get_rxfh_key_size(netdev));
+		yt_dbg(pdata, "%s, hash key\n", __func__);
+	}
+
+	return 0;
+}
+
+static int fxgmac_set_rxfh(struct net_device *netdev,
+			   struct ethtool_rxfh_param *rxfh,
+			   struct netlink_ext_ack *ack)
+{
+	u32 i, reta_entries = fxgmac_rss_indir_size(netdev);
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+
+	yt_dbg(pdata, "%s, indir=%lx, key=%lx, func=%02x\n", __func__,
+	       (unsigned long)rxfh->indir, (unsigned long)rxfh->key,
+	       rxfh->hfunc);
+
+	if (rxfh->hfunc)
+		return -EINVAL;
+
+	/* Fill out the redirection table */
+	if (rxfh->indir) {
+		/* double check user input. */
+		for (i = 0; i < reta_entries; i++)
+			if (rxfh->indir[i] >= FXGMAC_MAX_DMA_RX_CHANNELS)
+				return -EINVAL;
+
+		for (i = 0; i < reta_entries; i++)
+			pdata->rss_table[i] = rxfh->indir[i];
+
+		hw_ops->write_rss_lookup_table(pdata);
+	}
+
+	/* Fill out the rss hash key */
+	if (rxfh->key)
+		hw_ops->set_rss_hash_key(pdata, rxfh->key);
+
+	return 0;
+}
+
+static int fxgmac_get_rss_hash_opts(struct fxgmac_pdata *pdata,
+				    struct ethtool_rxnfc *cmd)
+{
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+	u32 reg_opt;
+
+	cmd->data = 0;
+	reg_opt = hw_ops->get_rss_options(pdata);
+	yt_dbg(pdata, "%s, hw=%02x, %02x\n", __func__, reg_opt,
+	       pdata->rss_options);
+
+	if (reg_opt != pdata->rss_options)
+		yt_dbg(pdata, "warning, options are not consistent\n");
+
+	/* Report default options for RSS */
+	switch (cmd->flow_type) {
+	case TCP_V4_FLOW:
+	case UDP_V4_FLOW:
+		if ((cmd->flow_type == TCP_V4_FLOW &&
+		     (pdata->rss_options & FXGMAC_RSS_TCP4TE)) ||
+		    (cmd->flow_type == UDP_V4_FLOW &&
+		     (pdata->rss_options & FXGMAC_RSS_UDP4TE))) {
+			cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+		}
+		fallthrough;
+	case SCTP_V4_FLOW:
+	case AH_ESP_V4_FLOW:
+	case AH_V4_FLOW:
+	case ESP_V4_FLOW:
+	case IPV4_FLOW:
+		if ((cmd->flow_type == TCP_V4_FLOW &&
+		     (pdata->rss_options & FXGMAC_RSS_TCP4TE)) ||
+		    (cmd->flow_type == UDP_V4_FLOW &&
+		     (pdata->rss_options & FXGMAC_RSS_UDP4TE)) ||
+		    (pdata->rss_options & BIT(FXGMAC_RSS_IP4TE))) {
+			cmd->data |= RXH_IP_SRC | RXH_IP_DST;
+		}
+		break;
+	case TCP_V6_FLOW:
+	case UDP_V6_FLOW:
+		if ((cmd->flow_type == TCP_V6_FLOW &&
+		     (pdata->rss_options & FXGMAC_RSS_TCP6TE)) ||
+		    (cmd->flow_type == UDP_V6_FLOW &&
+		     (pdata->rss_options & FXGMAC_RSS_UDP6TE))) {
+			cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+		}
+		fallthrough;
+	case SCTP_V6_FLOW:
+	case AH_ESP_V6_FLOW:
+	case AH_V6_FLOW:
+	case ESP_V6_FLOW:
+	case IPV6_FLOW:
+		if ((cmd->flow_type == TCP_V6_FLOW &&
+		     (pdata->rss_options & FXGMAC_RSS_TCP6TE)) ||
+		    (cmd->flow_type == UDP_V6_FLOW &&
+		     (pdata->rss_options & FXGMAC_RSS_UDP6TE)) ||
+		    (pdata->rss_options & FXGMAC_RSS_IP6TE)) {
+			cmd->data |= RXH_IP_SRC | RXH_IP_DST;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int fxgmac_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
+			    u32 *rule_locs)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(dev);
+	int ret = 0;
+
+	switch (cmd->cmd) {
+	case ETHTOOL_GRXRINGS:
+		cmd->data = pdata->rx_q_count;
+		yt_dbg(pdata, "%s, rx ring cnt\n", __func__);
+		break;
+	case ETHTOOL_GRXCLSRLCNT:
+		cmd->rule_cnt = 0;
+		yt_dbg(pdata, "%s, classify rule cnt\n", __func__);
+		break;
+	case ETHTOOL_GRXCLSRULE:
+		yt_dbg(pdata, "%s, classify rules\n", __func__);
+		break;
+	case ETHTOOL_GRXCLSRLALL:
+		cmd->rule_cnt = 0;
+		yt_dbg(pdata, "%s, classify both cnt and rules\n", __func__);
+		break;
+	case ETHTOOL_GRXFH:
+		ret = fxgmac_get_rss_hash_opts(pdata, cmd);
+		yt_dbg(pdata, "%s, hash options\n", __func__);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+
+#define UDP_RSS_FLAGS (FXGMAC_RSS_UDP4TE | FXGMAC_RSS_UDP6TE)
+
+static int fxgmac_set_rss_hash_opt(struct fxgmac_pdata *pdata,
+				   struct ethtool_rxnfc *nfc)
+{
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+	__u64 data = nfc->data;
+	u32 rssopt = 0;
+
+	yt_dbg(pdata, "%s nfc_data=%llx,cur opt=%x\n", __func__, data,
+	       pdata->rss_options);
+
+	/* For RSS, it does not support anything other than hashing
+	 * to queues on src,dst IPs and L4 ports
+	 */
+	if (data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3))
+		return -EINVAL;
+
+	switch (nfc->flow_type) {
+	case TCP_V4_FLOW:
+	case TCP_V6_FLOW:
+		/* default to TCP flow and do nothting */
+		if (!(data &
+		      (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)))
+			return -EINVAL;
+		if (nfc->flow_type == TCP_V4_FLOW)
+			rssopt |= BIT(FXGMAC_RSS_IP4TE) | FXGMAC_RSS_TCP4TE;
+		if (nfc->flow_type == TCP_V6_FLOW)
+			rssopt |= FXGMAC_RSS_IP6TE | FXGMAC_RSS_TCP6TE;
+		break;
+	case UDP_V4_FLOW:
+		if (!(data & (RXH_IP_SRC | RXH_IP_DST)))
+			return -EINVAL;
+		rssopt |= BIT(FXGMAC_RSS_IP4TE);
+		switch (data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+		case 0:
+			break;
+		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+			rssopt |= FXGMAC_RSS_UDP4TE;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case UDP_V6_FLOW:
+		if (!(data & (RXH_IP_SRC | RXH_IP_DST)))
+			return -EINVAL;
+		rssopt |= FXGMAC_RSS_IP6TE;
+
+		switch (data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+		case 0:
+			break;
+		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+			rssopt |= FXGMAC_RSS_UDP6TE;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case AH_ESP_V4_FLOW:
+	case AH_V4_FLOW:
+	case ESP_V4_FLOW:
+	case SCTP_V4_FLOW:
+	case AH_ESP_V6_FLOW:
+	case AH_V6_FLOW:
+	case ESP_V6_FLOW:
+	case SCTP_V6_FLOW:
+		if (!(data &
+		      (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)))
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* if options are changed, then update to hw */
+	if (rssopt != pdata->rss_options) {
+		if ((rssopt & UDP_RSS_FLAGS) &&
+		    !(pdata->rss_options & UDP_RSS_FLAGS))
+			yt_dbg(pdata,
+			       "enabling UDP RSS: fragmented packets may arrive out of order to the stack above.");
+
+		yt_dbg(pdata, "rss option changed from %x to %x\n",
+		       pdata->rss_options, rssopt);
+		pdata->rss_options = rssopt;
+		hw_ops->set_rss_options(pdata);
+	}
+
+	return 0;
+}
+
+static int fxgmac_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(dev);
+	int ret = -EOPNOTSUPP;
+
+	switch (cmd->cmd) {
+	case ETHTOOL_SRXCLSRLINS:
+		yt_dbg(pdata, "%s, rx cls rule insert-n\\a\n", __func__);
+		break;
+	case ETHTOOL_SRXCLSRLDEL:
+		yt_dbg(pdata, "%s, rx cls rule del-n\\a\n", __func__);
+		break;
+	case ETHTOOL_SRXFH:
+		yt_dbg(pdata, "%s, rx rss option\n", __func__);
+		ret = fxgmac_set_rss_hash_opt(pdata, cmd);
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static void fxgmac_get_ringparam(struct net_device *netdev,
+				 struct ethtool_ringparam *ring,
+				 struct kernel_ethtool_ringparam *kernel_ring,
+				 struct netlink_ext_ack *exact)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+
+	ring->rx_max_pending = FXGMAC_RX_DESC_CNT;
+	ring->tx_max_pending = FXGMAC_TX_DESC_CNT;
+	ring->rx_mini_max_pending = 0;
+	ring->rx_jumbo_max_pending = 0;
+	ring->rx_pending = pdata->rx_desc_count;
+	ring->tx_pending = pdata->tx_desc_count;
+	ring->rx_mini_pending = 0;
+	ring->rx_jumbo_pending = 0;
+}
+
+static int fxgmac_set_ringparam(struct net_device *netdev,
+				struct ethtool_ringparam *ring,
+				struct kernel_ethtool_ringparam *kernel_ring,
+				struct netlink_ext_ack *exact)
+
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	int ret;
+
+	if (pdata->dev_state != FXGMAC_DEV_START)
+		return 0;
+
+	mutex_lock(&pdata->mutex);
+	pdata->tx_desc_count = ring->tx_pending;
+	pdata->rx_desc_count = ring->rx_pending;
+
+	fxgmac_stop(pdata);
+	fxgmac_free_tx_data(pdata);
+	fxgmac_free_rx_data(pdata);
+	ret = fxgmac_channels_rings_alloc(pdata);
+	if (ret < 0)
+		goto unlock;
+
+	ret = fxgmac_start(pdata);
+
+unlock:
+	mutex_unlock(&pdata->mutex);
+	return ret;
+}
+
+static int fxgmac_get_regs_len(struct net_device __always_unused *netdev)
+{
+	return FXGMAC_PHY_REG_CNT * sizeof(u32);
+}
+
+static void fxgmac_get_regs(struct net_device *netdev,
+			    struct ethtool_regs *regs, void *p)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	u32 *regs_buff = p;
+
+	memset(p, 0, FXGMAC_PHY_REG_CNT * sizeof(u32));
+	for (u32 i = MII_BMCR; i < FXGMAC_PHY_REG_CNT; i++)
+		regs_buff[i] = phy_read(pdata->phydev, i);
+
+	regs->version = regs_buff[MII_PHYSID1] << 16 | regs_buff[MII_PHYSID2];
+}
+
+static void fxgmac_get_pauseparam(struct net_device *dev,
+				  struct ethtool_pauseparam *data)
+{
+	struct fxgmac_pdata *yt = netdev_priv(dev);
+	bool tx_pause, rx_pause;
+
+	phy_get_pause(yt->phydev, &tx_pause, &rx_pause);
+
+	data->autoneg = yt->phydev->autoneg;
+	data->tx_pause = tx_pause ? 1 : 0;
+	data->rx_pause = rx_pause ? 1 : 0;
+
+	yt_dbg(yt, "%s, rx=%d, tx=%d\n", __func__, data->rx_pause,
+	       data->tx_pause);
+}
+
+static int fxgmac_set_pauseparam(struct net_device *dev,
+				 struct ethtool_pauseparam *data)
+{
+	struct fxgmac_pdata *yt = netdev_priv(dev);
+
+	if (dev->mtu > ETH_DATA_LEN)
+		return -EOPNOTSUPP;
+
+	phy_set_asym_pause(yt->phydev, data->rx_pause, data->tx_pause);
+
+	yt_dbg(yt, "%s, autoneg=%d, rx=%d, tx=%d\n", __func__,
+	       data->autoneg, data->rx_pause, data->tx_pause);
+
+	return 0;
+}
+
+static void fxgmac_ethtool_get_strings(struct net_device *netdev, u32 stringset,
+				       u8 *data)
+{
+	switch (stringset) {
+	case ETH_SS_STATS:
+		for (u32 i = 0; i < FXGMAC_STATS_COUNT; i++) {
+			memcpy(data, fxgmac_gstring_stats[i].stat_string,
+			       strlen(fxgmac_gstring_stats[i].stat_string));
+			data += ETH_GSTRING_LEN;
+		}
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+}
+
+static int fxgmac_ethtool_get_sset_count(struct net_device *netdev,
+					 int stringset)
+{
+	int ret;
+
+	switch (stringset) {
+	case ETH_SS_STATS:
+		ret = FXGMAC_STATS_COUNT;
+		break;
+
+	default:
+		ret = -EOPNOTSUPP;
+	}
+
+	return ret;
+}
+
+static void fxgmac_ethtool_get_ethtool_stats(struct net_device *netdev,
+					     struct ethtool_stats *stats,
+					     u64 *data)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+
+	if (!test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->powerstate))
+		pdata->hw_ops.read_mmc_stats(pdata);
+
+	for (u32 i = 0; i < FXGMAC_STATS_COUNT; i++)
+		*data++ = *(u64 *)((u8 *)pdata +
+				   fxgmac_gstring_stats[i].stat_offset);
+}
+
+static int fxgmac_ethtool_reset(struct net_device *netdev, u32 *flag)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	int ret = 0;
+	u32 val;
+
+	val = *flag & (ETH_RESET_ALL | ETH_RESET_PHY);
+	if (!val) {
+		yt_err(pdata, "Operation not support.\n");
+		return -EOPNOTSUPP;
+	}
+
+	switch (*flag) {
+	case ETH_RESET_ALL:
+		fxgmac_restart(pdata);
+		*flag = 0;
+		break;
+	case ETH_RESET_PHY:
+		/* Power off and on the phy in order to properly
+		 * configure the MAC timing
+		 */
+		ret = phy_modify(pdata->phydev, MII_BMCR, BMCR_PDOWN,
+				 BMCR_PDOWN);
+		if (ret < 0)
+			return ret;
+		fsleep(9000);
+
+		ret = phy_modify(pdata->phydev, MII_BMCR, BMCR_PDOWN, 0);
+		if (ret < 0)
+			return ret;
+		*flag = 0;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static const struct ethtool_ops fxgmac_ethtool_ops = {
+	.get_drvinfo			= fxgmac_ethtool_get_drvinfo,
+	.get_link			= ethtool_op_get_link,
+	.get_msglevel			= fxgmac_ethtool_get_msglevel,
+	.set_msglevel			= fxgmac_ethtool_set_msglevel,
+	.get_channels			= fxgmac_ethtool_get_channels,
+	.get_coalesce			= fxgmac_ethtool_get_coalesce,
+	.set_coalesce			= fxgmac_ethtool_set_coalesce,
+	.reset				= fxgmac_ethtool_reset,
+	.supported_coalesce_params	= ETHTOOL_COALESCE_USECS,
+	.get_strings			= fxgmac_ethtool_get_strings,
+	.get_sset_count			= fxgmac_ethtool_get_sset_count,
+	.get_ethtool_stats		= fxgmac_ethtool_get_ethtool_stats,
+	.get_regs_len			= fxgmac_get_regs_len,
+	.get_regs			= fxgmac_get_regs,
+	.get_ringparam			= fxgmac_get_ringparam,
+	.set_ringparam			= fxgmac_set_ringparam,
+	.get_rxnfc			= fxgmac_get_rxnfc,
+	.set_rxnfc			= fxgmac_set_rxnfc,
+	.get_rxfh_indir_size		= fxgmac_rss_indir_size,
+	.get_rxfh_key_size		= fxgmac_get_rxfh_key_size,
+	.get_rxfh			= fxgmac_get_rxfh,
+	.set_rxfh			= fxgmac_set_rxfh,
+	.nway_reset			= phy_ethtool_nway_reset,
+	.get_link_ksettings		= phy_ethtool_get_link_ksettings,
+	.set_link_ksettings		= phy_ethtool_set_link_ksettings,
+	.get_pauseparam			= fxgmac_get_pauseparam,
+	.set_pauseparam			= fxgmac_set_pauseparam,
+};
+
+const struct ethtool_ops *fxgmac_get_ethtool_ops(void)
+{
+	return &fxgmac_ethtool_ops;
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 14/21] motorcomm:yt6801: Implement the WOL function of ethtool_ops
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (5 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 13/21] motorcomm:yt6801: Implement some ethtool_ops function Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 15/21] motorcomm:yt6801: Implement pci_driver suspend and resume Frank Sae
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement the .get_wol and .set_wol callback function.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../motorcomm/yt6801/yt6801_ethtool.c         | 169 +++++
 .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 576 ++++++++++++++++++
 .../ethernet/motorcomm/yt6801/yt6801_net.c    | 118 ++++
 3 files changed, 863 insertions(+)

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c
index 7989ccbc3..af643a16a 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c
@@ -565,6 +565,173 @@ static int fxgmac_set_ringparam(struct net_device *netdev,
 	return ret;
 }
 
+static void fxgmac_get_wol(struct net_device *netdev,
+			   struct ethtool_wolinfo *wol)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+
+	wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC |
+			 WAKE_ARP | WAKE_PHY;
+
+	wol->wolopts = 0;
+	if (!(pdata->hw_feat.rwk) || !device_can_wakeup(pdata->dev)) {
+		yt_err(pdata, "%s, pci does not support wakeup.\n", __func__);
+		return;
+	}
+
+	wol->wolopts = pdata->wol;
+}
+
+#pragma pack(1)
+/* it's better to make this struct's size to 128byte. */
+struct pattern_packet {
+	u8 ether_daddr[ETH_ALEN];
+	u8 ether_saddr[ETH_ALEN];
+	u16 ether_type;
+
+	__be16 ar_hrd;		/* format of hardware address  */
+	__be16 ar_pro;		/* format of protocol          */
+	u8 ar_hln;		/* length of hardware address  */
+	u8 ar_pln;		/* length of protocol address  */
+	__be16 ar_op;		/* ARP opcode (command)        */
+	u8 ar_sha[ETH_ALEN];	/* sender hardware address     */
+	u8 ar_sip[4];		/* sender IP address           */
+	u8 ar_tha[ETH_ALEN];	/* target hardware address     */
+	u8 ar_tip[4];		/* target IP address           */
+
+	u8 reverse[86];
+};
+
+#pragma pack()
+
+static int fxgmac_set_pattern_data(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+	u8 type_offset, tip_offset, op_offset;
+	struct wol_bitmap_pattern pattern[4];
+	struct pattern_packet packet;
+	u32 ip_addr, i = 0;
+
+	memset(pattern, 0, sizeof(struct wol_bitmap_pattern) * 4);
+
+	/* Config ucast */
+	if (pdata->wol & WAKE_UCAST) {
+		pattern[i].mask_info[0] = 0x3F;
+		pattern[i].mask_size = sizeof(pattern[0].mask_info);
+		memcpy(pattern[i].pattern_info, pdata->mac_addr, ETH_ALEN);
+		pattern[i].pattern_offset = 0;
+		i++;
+	}
+
+	/* Config bcast */
+	if (pdata->wol & WAKE_BCAST) {
+		pattern[i].mask_info[0] = 0x3F;
+		pattern[i].mask_size = sizeof(pattern[0].mask_info);
+		memset(pattern[i].pattern_info, 0xFF, ETH_ALEN);
+		pattern[i].pattern_offset = 0;
+		i++;
+	}
+
+	/* Config mcast */
+	if (pdata->wol & WAKE_MCAST) {
+		pattern[i].mask_info[0] = 0x7;
+		pattern[i].mask_size = sizeof(pattern[0].mask_info);
+		pattern[i].pattern_info[0] = 0x1;
+		pattern[i].pattern_info[1] = 0x0;
+		pattern[i].pattern_info[2] = 0x5E;
+		pattern[i].pattern_offset = 0;
+		i++;
+	}
+
+	/* Config arp */
+	if (pdata->wol & WAKE_ARP) {
+		memset(pattern[i].mask_info, 0, sizeof(pattern[0].mask_info));
+		type_offset = offsetof(struct pattern_packet, ar_pro);
+		pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8;
+		type_offset++;
+		pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8;
+		op_offset = offsetof(struct pattern_packet, ar_op);
+		pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8;
+		op_offset++;
+		pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8;
+		tip_offset = offsetof(struct pattern_packet, ar_tip);
+		pattern[i].mask_info[tip_offset / 8] |= 1 << tip_offset % 8;
+		tip_offset++;
+		pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8;
+		tip_offset++;
+		pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8;
+		tip_offset++;
+		pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8;
+
+		packet.ar_pro = 0x0 << 8 | 0x08;
+		/* ARP type is 0x0800, notice that ar_pro and ar_op is
+		 * big endian
+		 */
+		packet.ar_op = 0x1 << 8;
+		/* 1 is arp request,2 is arp replay, 3 is rarp request,
+		 * 4 is rarp replay
+		 */
+		ip_addr = fxgmac_get_netdev_ip4addr(pdata);
+		packet.ar_tip[0] = ip_addr & 0xFF;
+		packet.ar_tip[1] = (ip_addr >> 8) & 0xFF;
+		packet.ar_tip[2] = (ip_addr >> 16) & 0xFF;
+		packet.ar_tip[3] = (ip_addr >> 24) & 0xFF;
+		memcpy(pattern[i].pattern_info, &packet, MAX_PATTERN_SIZE);
+		pattern[i].mask_size = sizeof(pattern[0].mask_info);
+		pattern[i].pattern_offset = 0;
+		i++;
+	}
+
+	return hw_ops->set_wake_pattern(pdata, pattern, i);
+}
+
+static int fxgmac_set_wol(struct net_device *netdev,
+			  struct ethtool_wolinfo *wol)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	int ret;
+
+	if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_FILTER)) {
+		yt_err(pdata, "%s, not supported wol options, 0x%x\n", __func__,
+		       wol->wolopts);
+		return -EOPNOTSUPP;
+	}
+
+	if (!(pdata->hw_feat.rwk)) {
+		yt_err(pdata, "%s, hw wol feature is n/a\n", __func__);
+		return wol->wolopts ? -EOPNOTSUPP : 0;
+	}
+
+	pdata->wol = 0;
+	if (wol->wolopts & WAKE_UCAST)
+		pdata->wol |= WAKE_UCAST;
+
+	if (wol->wolopts & WAKE_MCAST)
+		pdata->wol |= WAKE_MCAST;
+
+	if (wol->wolopts & WAKE_BCAST)
+		pdata->wol |= WAKE_BCAST;
+
+	if (wol->wolopts & WAKE_MAGIC)
+		pdata->wol |= WAKE_MAGIC;
+
+	if (wol->wolopts & WAKE_PHY)
+		pdata->wol |= WAKE_PHY;
+
+	if (wol->wolopts & WAKE_ARP)
+		pdata->wol |= WAKE_ARP;
+
+	ret = fxgmac_set_pattern_data(pdata);
+	if (ret < 0)
+		return ret;
+
+	ret = fxgmac_config_wol(pdata, (!!(pdata->wol)));
+	yt_dbg(pdata, "%s, opt=0x%x, wol=0x%x\n", __func__, wol->wolopts,
+	       pdata->wol);
+
+	return ret;
+}
+
 static int fxgmac_get_regs_len(struct net_device __always_unused *netdev)
 {
 	return FXGMAC_PHY_REG_CNT * sizeof(u32);
@@ -725,6 +892,8 @@ static const struct ethtool_ops fxgmac_ethtool_ops = {
 	.get_rxfh_key_size		= fxgmac_get_rxfh_key_size,
 	.get_rxfh			= fxgmac_get_rxfh,
 	.set_rxfh			= fxgmac_set_rxfh,
+	.get_wol			= fxgmac_get_wol,
+	.set_wol			= fxgmac_set_wol,
 	.nway_reset			= phy_ethtool_nway_reset,
 	.get_link_ksettings		= phy_ethtool_get_link_ksettings,
 	.set_link_ksettings		= phy_ethtool_set_link_ksettings,
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
index a70fa4ede..bd3036625 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
@@ -1399,6 +1399,187 @@ static void fxgmac_config_rss(struct fxgmac_pdata *pdata)
 		fxgmac_disable_rss(pdata);
 }
 
+static void fxgmac_update_aoe_ipv4addr(struct fxgmac_pdata *pdata, u8 *ip_addr)
+{
+	u32 val, ipval = 0;
+
+	if (ip_addr) {
+		ipval = (ip_addr[0] << 24) | (ip_addr[1] << 16) |
+			(ip_addr[2] << 8) | (ip_addr[3] << 0);
+		yt_dbg(pdata,
+		       "%s, covert IP dotted-addr %s to binary 0x%08x ok.\n",
+		       __func__, ip_addr, cpu_to_be32(ipval));
+	} else {
+		/* Get ipv4 addr from net device */
+		ipval = fxgmac_get_netdev_ip4addr(pdata);
+		yt_dbg(pdata, "%s, Get net device binary IP ok, 0x%08x\n",
+		       __func__, cpu_to_be32(ipval));
+
+		ipval = cpu_to_be32(ipval);
+	}
+
+	val = rd32_mac(pdata, MAC_ARP_PROTO_ADDR);
+	if (val != (ipval)) {
+		wr32_mac(pdata, ipval, MAC_ARP_PROTO_ADDR);
+		yt_dbg(pdata,
+		       "%s, update arp ipaddr reg from 0x%08x to 0x%08x\n",
+		       __func__, val, ipval);
+	}
+}
+
+static void fxgmac_enable_arp_offload(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN, 1);
+	wr32_mac(pdata, val, MAC_CR);
+}
+
+static void fxgmac_disable_arp_offload(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN, 0);
+	wr32_mac(pdata, val, MAC_CR);
+}
+
+/**
+ * fxgmac_set_ns_offload - config register for NS offload function
+ * @pdata: board private structure
+ * @index: 0~1, index to NS look up table. one entry of the lut is like
+ *   this |remote|solicited|target0|target1|
+ * @remote_addr: ipv6 addr where yt6801 gets the NS solicitation pkt(request).
+ *   in common, it is 0 to match any remote machine.
+ * @solicited_addr: the solicited node multicast group address which
+ *   yt6801 computes and joins.
+ * @target_addr1: it is the target address in NS solicitation pkt.
+ * @target_addr2:second target address, any address (with ast 6B same with
+ *  target address)
+ * @mac_addr: it is the target address in NS solicitation pkt.
+ */
+static void
+fxgmac_set_ns_offload(struct fxgmac_pdata *pdata, unsigned int index,
+		      unsigned char *remote_addr, unsigned char *solicited_addr,
+		      unsigned char *target_addr1, unsigned char *target_addr2,
+		      unsigned char *mac_addr)
+{
+	u32 base = (NS_LUT_MAC_ADDR_CTL - NS_LUT_ROMOTE0 + 4) * index;
+	u32 address[4], mac_hi, mac_lo;
+	u8 remote_not_zero = 0;
+	u32 val;
+
+	val = rd32_mem(pdata, NS_TPID_PRO);
+	fxgmac_set_bits(&val, NS_TPID_PRO_STPID_POS, NS_TPID_PRO_STPID_LEN,
+			0X8100);
+	fxgmac_set_bits(&val, NS_TPID_PRO_CTPID_POS, NS_TPID_PRO_CTPID_LEN,
+			0X9100);
+	wr32_mem(pdata, val, NS_TPID_PRO);
+
+	val = rd32_mem(pdata, base + NS_LUT_MAC_ADDR_CTL);
+	fxgmac_set_bits(&val, NS_LUT_DST_CMP_TYPE_POS, NS_LUT_DST_CMP_TYPE_LEN,
+			1);
+	fxgmac_set_bits(&val, NS_LUT_DST_IGNORED_POS, NS_LUT_DST_IGNORED_LEN,
+			1);
+	fxgmac_set_bits(&val, NS_LUT_REMOTE_AWARED_POS,
+			NS_LUT_REMOTE_AWARED_LEN, 1);
+	fxgmac_set_bits(&val, NS_LUT_TARGET_ISANY_POS, NS_LUT_TARGET_ISANY_LEN,
+			0);
+	wr32_mem(pdata, val, base + NS_LUT_MAC_ADDR_CTL);
+
+	for (u32 i = 0; i < 16 / 4; i++) {
+		address[i] = (remote_addr[i * 4 + 0] << 24) |
+			     (remote_addr[i * 4 + 1] << 16) |
+			     (remote_addr[i * 4 + 2] << 8) |
+			     (remote_addr[i * 4 + 3] << 0);
+		wr32_mem(pdata, address[i], base + NS_LUT_ROMOTE0 + 4 * i);
+		if (address[i])
+			remote_not_zero = 1;
+
+		address[i] = (target_addr1[i * 4 + 0] << 24) |
+			     (target_addr1[i * 4 + 1] << 16) |
+			     (target_addr1[i * 4 + 2] << 8) |
+			     (target_addr1[i * 4 + 3] << 0);
+		wr32_mem(pdata, address[i], base + NS_LUT_TARGET0 + 4 * i);
+		address[i] = (solicited_addr[i * 4 + 0] << 24) |
+			     (solicited_addr[i * 4 + 1] << 16) |
+			     (solicited_addr[i * 4 + 2] << 8) |
+			     (solicited_addr[i * 4 + 3] << 0);
+		wr32_mem(pdata, address[i], base + NS_LUT_SOLICITED0 + 4 * i);
+		address[i] = (target_addr2[i * 4 + 0] << 24) |
+			     (target_addr2[i * 4 + 1] << 16) |
+			     (target_addr2[i * 4 + 2] << 8) |
+			     (target_addr2[i * 4 + 3] << 0);
+		wr32_mem(pdata, address[i],
+			 0X10 * index + NS_LUT_TARGET4 + 4 * i);
+	}
+	mac_hi = (mac_addr[0] << 24) | (mac_addr[1] << 16) |
+		      (mac_addr[2] << 8) | (mac_addr[3] << 0);
+	mac_lo = (mac_addr[4] << 8) | (mac_addr[5] << 0);
+	wr32_mem(pdata, mac_hi, base + NS_LUT_MAC_ADDR);
+
+	val = rd32_mem(pdata, base + NS_LUT_MAC_ADDR_CTL);
+	fxgmac_set_bits(&val, NS_LUT_MAC_ADDR_LOW_POS, NS_LUT_MAC_ADDR_LOW_LEN,
+			mac_lo);
+	if (remote_not_zero == 0)
+		fxgmac_set_bits(&val, NS_LUT_REMOTE_AWARED_POS,
+				NS_LUT_REMOTE_AWARED_LEN, 0);
+	else
+		fxgmac_set_bits(&val, NS_LUT_REMOTE_AWARED_POS,
+				NS_LUT_REMOTE_AWARED_LEN, 1);
+	wr32_mem(pdata, val, base + NS_LUT_MAC_ADDR_CTL);
+}
+
+static void fxgmac_update_ns_offload_ipv6addr(struct fxgmac_pdata *pdata,
+					      unsigned int param)
+{
+	struct net_device *netdev = pdata->netdev;
+	unsigned char addr_buf[5][16];
+	static u8 ns_offload_tab_idx;
+	unsigned char *solicited_addr;
+	unsigned char *target_addr1;
+	unsigned char *remote_addr;
+	unsigned char *mac_addr;
+
+	remote_addr = (unsigned char *)&addr_buf[0][0];
+	solicited_addr = (unsigned char *)&addr_buf[1][0];
+	target_addr1 = (unsigned char *)&addr_buf[2][0];
+	mac_addr = (unsigned char *)&addr_buf[4][0];
+
+	param &= (FXGMAC_NS_IFA_LOCAL_LINK | FXGMAC_NS_IFA_GLOBAL_UNICAST);
+	/* Get ipv6 addr from net device */
+	if (fxgmac_get_netdev_ip6addr(pdata, target_addr1, solicited_addr,
+				      param) == NULL) {
+		yt_err(pdata,
+		       "%s, get ipv6 addr with err and ignore NS offload.\n",
+		       __func__);
+		return;
+	}
+
+	yt_dbg(pdata, "%s, IPv6 local-link=%pI6, solicited =%pI6\n", __func__,
+	       target_addr1, solicited_addr);
+
+	memcpy(mac_addr, netdev->dev_addr, netdev->addr_len);
+	yt_dbg(pdata, "ns_tab idx=%d, %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       ns_offload_tab_idx, mac_addr[0], mac_addr[1], mac_addr[2],
+	       mac_addr[3], mac_addr[4], mac_addr[5]);
+
+	memset(remote_addr, 0, 16);
+	fxgmac_set_ns_offload(pdata, ns_offload_tab_idx++, remote_addr,
+			      solicited_addr, target_addr1, target_addr1,
+			      mac_addr);
+
+#define NS_OFFLOAD_TAB_MAX_IDX	2
+	if (ns_offload_tab_idx >= NS_OFFLOAD_TAB_MAX_IDX)
+		ns_offload_tab_idx = 0;
+}
+
+static void fxgmac_enable_ns_offload(struct fxgmac_pdata *pdata)
+{
+	wr32_mem(pdata, 0X00000011, NS_OF_GLB_CTL);
+}
+
 static int fxgmac_check_wake_pattern_fifo_pointer(struct fxgmac_pdata *pdata)
 {
 	u32 val;
@@ -1419,6 +1600,390 @@ static int fxgmac_check_wake_pattern_fifo_pointer(struct fxgmac_pdata *pdata)
 	return 0;
 }
 
+static int fxgmac_set_wake_pattern_mask(struct fxgmac_pdata *pdata,
+					u32 filter_index, u8 register_index,
+					u32 data)
+{
+	const u16 address_offset[16][3] = {
+		{0x1020, 0x1024, 0x1028},
+		{0x102c, 0x1030, 0x1034},
+		{0x1038, 0x103c, 0x1040},
+		{0x1044, 0x1050, 0x1054},
+		{0x1058, 0x105c, 0x1060},
+		{0x1064, 0x1068, 0x106c},
+		{0x1070, 0x1074, 0x1078},
+		{0x107c, 0x1080, 0x1084},
+		{0x1088, 0x108c, 0x1090},
+		{0x1134, 0x113c, 0x1140},
+		{0x1208, 0x1200, 0x1204},
+		{0x1218, 0x1210, 0x1214},
+		{0x1228, 0x1220, 0x1224},
+		{0x1238, 0x1230, 0x1234},
+		{0x1248, 0x1240, 0x1244},
+		{0x1258, 0x1250, 0x1254},
+	};
+	if (filter_index > 15 || register_index > 2) {
+		yt_err(pdata,
+		       "%s, xx is over range, filter:%d, register:0x%x\n",
+		       __func__, filter_index, register_index);
+		return -EINVAL;
+	}
+	wr32_mem(pdata, data, address_offset[filter_index][register_index]);
+	return 0;
+}
+
+union type16 {
+	u16 raw;
+	struct {
+		u16 bit_0 : 1;
+		u16 bit_1 : 1;
+		u16 bit_2 : 1;
+		u16 bit_3 : 1;
+		u16 bit_4 : 1;
+		u16 bit_5 : 1;
+		u16 bit_6 : 1;
+		u16 bit_7 : 1;
+		u16 bit_8 : 1;
+		u16 bit_9 : 1;
+		u16 bit_10 : 1;
+		u16 bit_11 : 1;
+		u16 bit_12 : 1;
+		u16 bit_13 : 1;
+		u16 bit_14 : 1;
+		u16 bit_15 : 1;
+	} bits;
+};
+
+union type8 {
+	u16 raw;
+	struct {
+		u16 bit_0 : 1;
+		u16 bit_1 : 1;
+		u16 bit_2 : 1;
+		u16 bit_3 : 1;
+		u16 bit_4 : 1;
+		u16 bit_5 : 1;
+		u16 bit_6 : 1;
+		u16 bit_7 : 1;
+	} bits;
+};
+
+static u16 wol_crc16(u8 *frame, u16 uslen)
+{
+	union type16 crc, crc_comb;
+	union type8 next_crc, rrpe_data;
+
+	next_crc.raw = 0;
+	crc.raw = 0xffff;
+	for (u32 i = 0; i < uslen; i++) {
+		rrpe_data.raw = frame[i];
+		next_crc.bits.bit_0 = crc.bits.bit_15 ^ rrpe_data.bits.bit_0;
+		next_crc.bits.bit_1 = crc.bits.bit_14 ^ next_crc.bits.bit_0 ^
+				      rrpe_data.bits.bit_1;
+		next_crc.bits.bit_2 = crc.bits.bit_13 ^ next_crc.bits.bit_1 ^
+				      rrpe_data.bits.bit_2;
+		next_crc.bits.bit_3 = crc.bits.bit_12 ^ next_crc.bits.bit_2 ^
+				      rrpe_data.bits.bit_3;
+		next_crc.bits.bit_4 = crc.bits.bit_11 ^ next_crc.bits.bit_3 ^
+				      rrpe_data.bits.bit_4;
+		next_crc.bits.bit_5 = crc.bits.bit_10 ^ next_crc.bits.bit_4 ^
+				      rrpe_data.bits.bit_5;
+		next_crc.bits.bit_6 = crc.bits.bit_9 ^ next_crc.bits.bit_5 ^
+				      rrpe_data.bits.bit_6;
+		next_crc.bits.bit_7 = crc.bits.bit_8 ^ next_crc.bits.bit_6 ^
+				      rrpe_data.bits.bit_7;
+
+		crc_comb.bits.bit_15 = crc.bits.bit_7 ^ next_crc.bits.bit_7;
+		crc_comb.bits.bit_14 = crc.bits.bit_6;
+		crc_comb.bits.bit_13 = crc.bits.bit_5;
+		crc_comb.bits.bit_12 = crc.bits.bit_4;
+		crc_comb.bits.bit_11 = crc.bits.bit_3;
+		crc_comb.bits.bit_10 = crc.bits.bit_2;
+		crc_comb.bits.bit_9 = crc.bits.bit_1 ^ next_crc.bits.bit_0;
+		crc_comb.bits.bit_8 = crc.bits.bit_0 ^ next_crc.bits.bit_1;
+		crc_comb.bits.bit_7 = next_crc.bits.bit_0 ^ next_crc.bits.bit_2;
+		crc_comb.bits.bit_6 = next_crc.bits.bit_1 ^ next_crc.bits.bit_3;
+		crc_comb.bits.bit_5 = next_crc.bits.bit_2 ^ next_crc.bits.bit_4;
+		crc_comb.bits.bit_4 = next_crc.bits.bit_3 ^ next_crc.bits.bit_5;
+		crc_comb.bits.bit_3 = next_crc.bits.bit_4 ^ next_crc.bits.bit_6;
+		crc_comb.bits.bit_2 = next_crc.bits.bit_5 ^ next_crc.bits.bit_7;
+		crc_comb.bits.bit_1 = next_crc.bits.bit_6;
+		crc_comb.bits.bit_0 = next_crc.bits.bit_7;
+		crc.raw = crc_comb.raw;
+	}
+	return crc.raw;
+}
+
+static void __write_filter(struct fxgmac_pdata *pdata, u8 i, u8 n)
+{
+	u8 *mask = &pdata->pattern[i * 4 + n].mask_info[0];
+	u32 val;
+
+	val = (mask[3] & 0x7f << 24) | (mask[2] << 16) | (mask[1] << 8) |
+	      (mask[0] << 0);
+
+	wr32_mac(pdata, val, MAC_RWK_PAC);
+}
+
+static u32 __set_filter_addr_type(struct fxgmac_pdata *pdata, u8 i, u8 n,
+				  u32 total_cnt, u32 pattern_cnt)
+{
+	struct wol_bitmap_pattern *pattern = &pdata->pattern[i * 4 + n];
+	u32 val;
+
+	/* Set filter enable bit. */
+	val = ((i * 4 + n) < total_cnt) ? (0x1 << 8 * n) : 0x0;
+
+	/* Set filter address type, 0- unicast, 1 - multicast. */
+	val |= (i * 4 + n >= total_cnt)		 ? 0x0 :
+	       (i * 4 + n >= pattern_cnt)	 ? (0x1 << (3 + 8 * n)) :
+	       pattern->pattern_offset		 ? 0x0 :
+	       !(pattern->mask_info[0] & 0x01)	 ? 0x0 :
+	       (pattern->pattern_info[0] & 0x01) ? (0x1 << (3 + 8 * n)) :
+							 0x0;
+	return val;
+}
+
+static u32 __wake_pattern_mask_val(struct wol_bitmap_pattern *pattern, u8 n)
+{
+	u8 *mask = &pattern->mask_info[n * 4];
+	u32 val;
+
+	val = ((mask[7] & 0x7f) << (24 + 1)) | (mask[6] << (16 + 1)) |
+	      (mask[5] << (8 + 1)) | (mask[4] << (0 + 1)) |
+	      ((mask[3] & 0x80) >> 7);
+
+	return val;
+}
+
+static int fxgmac_set_wake_pattern(struct fxgmac_pdata *pdata,
+				   struct wol_bitmap_pattern *wol_pattern,
+				   u32 pattern_cnt)
+{
+	u32 total_cnt = 0, inherited_cnt = 0, val;
+	struct wol_bitmap_pattern *pattern;
+	u16 map_index, mask_index;
+	u8 mask[MAX_PATTERN_SIZE];
+	u32 i, j, z;
+
+	if (pattern_cnt > MAX_PATTERN_COUNT) {
+		yt_err(pdata, "%s, %d patterns exceed %d, not supported!\n",
+		       __func__, pattern_cnt, MAX_PATTERN_COUNT);
+		return -EINVAL;
+	}
+
+	/* Reset the FIFO head pointer. */
+	if (fxgmac_check_wake_pattern_fifo_pointer(pdata)) {
+		yt_err(pdata, "%s, remote pattern array pointer is not be 0\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < pattern_cnt; i++) {
+		pattern = &pdata->pattern[i];
+		memcpy(pattern, wol_pattern + i, sizeof(wol_pattern[0]));
+
+		if (pattern_cnt + inherited_cnt >= MAX_PATTERN_COUNT)
+			continue;
+
+		if (wol_pattern[i].pattern_offset ||
+		    !(wol_pattern[i].mask_info[0] & 0x01)) {
+			pattern = &pdata->pattern[pattern_cnt + inherited_cnt];
+			memcpy(pattern, wol_pattern + i,
+			       sizeof(wol_pattern[0]));
+			inherited_cnt++;
+		}
+	}
+	total_cnt = pattern_cnt + inherited_cnt;
+
+	/* Calculate the crc-16 of the mask pattern */
+	for (i = 0; i < total_cnt; i++) {
+		/* Please program pattern[i] to NIC for pattern match wakeup.
+		 * pattern_size, pattern_info, mask_info
+		 */
+		mask_index = 0;
+		map_index = 0;
+		pattern = &pdata->pattern[i];
+		for (j = 0; j < pattern->mask_size; j++) {
+			for (z = 0;
+			     z < ((j == (MAX_PATTERN_SIZE / 8 - 1)) ? 7 : 8);
+			     z++) {
+				if (pattern->mask_info[j] & (0x01 << z)) {
+					mask[map_index] =
+						pattern->pattern_info
+							[pattern->pattern_offset +
+							 mask_index];
+					map_index++;
+				}
+				mask_index++;
+			}
+		}
+		/* Calculate  the crc-16 of the mask pattern */
+		pattern->pattern_crc = wol_crc16(mask, map_index);
+		memset(mask, 0, sizeof(mask));
+	}
+
+	/* Write patterns by FIFO block. */
+	for (i = 0; i < (total_cnt + 3) / 4; i++) {
+		/* 1. Write the first 4Bytes of Filter. */
+		__write_filter(pdata, i, 0);
+		__write_filter(pdata, i, 1);
+		__write_filter(pdata, i, 2);
+		__write_filter(pdata, i, 3);
+
+		/* 2. Write the Filter Command. */
+		val = 0;
+		val |= __set_filter_addr_type(pdata, i, 0, total_cnt,
+					      pattern_cnt);
+		val |= __set_filter_addr_type(pdata, i, 1, total_cnt,
+					      pattern_cnt);
+		val |= __set_filter_addr_type(pdata, i, 2, total_cnt,
+					      pattern_cnt);
+		val |= __set_filter_addr_type(pdata, i, 3, total_cnt,
+					      pattern_cnt);
+		wr32_mac(pdata, val, MAC_RWK_PAC);
+
+		/* 3. Write the mask offset. */
+		val = (pdata->pattern[i * 4 + 3].pattern_offset << 24) |
+		      (pdata->pattern[i * 4 + 2].pattern_offset << 16) |
+		      (pdata->pattern[i * 4 + 1].pattern_offset << 8) |
+		      (pdata->pattern[i * 4 + 0].pattern_offset << 0);
+		wr32_mac(pdata, val, MAC_RWK_PAC);
+
+		/* 4. Write the masked data CRC. */
+		val = (pdata->pattern[i * 4 + 1].pattern_crc << 16) |
+		      (pdata->pattern[i * 4 + 0].pattern_crc << 0);
+		wr32_mac(pdata, val, MAC_RWK_PAC);
+		val = (pdata->pattern[i * 4 + 3].pattern_crc << 16) |
+		      (pdata->pattern[i * 4 + 2].pattern_crc << 0);
+		wr32_mac(pdata, val, MAC_RWK_PAC);
+	}
+
+	for (i = 0; i < total_cnt; i++) {
+		/* Global  manager  regitster  mask bit 31~62 */
+		pattern = &pdata->pattern[i];
+		val = __wake_pattern_mask_val(pattern, 0);
+		fxgmac_set_wake_pattern_mask(pdata, i, 0, val);
+
+		/* Global  manager  regitster  mask bit 63~94 */
+		val = __wake_pattern_mask_val(pattern, 1);
+		fxgmac_set_wake_pattern_mask(pdata, i, 1, val);
+
+		/* Global  manager regitster  mask bit 95~126 */
+		val = __wake_pattern_mask_val(pattern, 2);
+		fxgmac_set_wake_pattern_mask(pdata, i, 2, val);
+	}
+
+	return 0;
+}
+
+static void fxgmac_enable_wake_pattern(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_PMT_STA);
+	fxgmac_set_bits(&val, MAC_PMT_STA_RWKFILTERST_POS,
+			MAC_PMT_STA_RWKFILTERST_LEN, 1);
+	fxgmac_set_bits(&val, MAC_PMT_STA_RWKPKTEN_POS,
+			MAC_PMT_STA_RWKPKTEN_LEN, 1);
+	wr32_mac(pdata, val, MAC_PMT_STA);
+
+	val = rd32_mem(pdata, WOL_CTRL);
+	fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1);
+	wr32_mem(pdata, val, WOL_CTRL);
+}
+
+static void fxgmac_disable_wake_pattern(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_PMT_STA);
+	fxgmac_set_bits(&val, MAC_PMT_STA_RWKFILTERST_POS,
+			MAC_PMT_STA_RWKFILTERST_LEN, 1);
+	fxgmac_set_bits(&val, MAC_PMT_STA_RWKPKTEN_POS,
+			MAC_PMT_STA_RWKPKTEN_LEN, 0);
+	wr32_mac(pdata, val, MAC_PMT_STA);
+
+	val = rd32_mem(pdata, WOL_CTRL);
+	fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0);
+	wr32_mem(pdata, val, WOL_CTRL);
+}
+
+static void fxgmac_enable_wake_magic_pattern(struct fxgmac_pdata *pdata)
+{
+	struct pci_dev *pdev = to_pci_dev(pdata->dev);
+	u16 pm_ctrl;
+	u32 val;
+	int pos;
+
+	val = rd32_mac(pdata, MAC_PMT_STA);
+	fxgmac_set_bits(&val, MAC_PMT_STA_MGKPKTEN_POS,
+			MAC_PMT_STA_MGKPKTEN_LEN, 1);
+	wr32_mac(pdata, val, MAC_PMT_STA);
+
+	val = rd32_mem(pdata, WOL_CTRL);
+	fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1);
+	wr32_mem(pdata, val, WOL_CTRL);
+
+	pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+	if (!pos) {
+		yt_err(pdata, "Unable to find Power Management Capabilities\n");
+		return;
+	}
+
+	pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pm_ctrl);
+	pm_ctrl |= PCI_PM_CTRL_PME_ENABLE;
+	pci_write_config_word(pdev, pos + PCI_PM_CTRL, pm_ctrl);
+}
+
+static void fxgmac_disable_wake_magic_pattern(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mem(pdata, WOL_CTRL);
+	fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0);
+	wr32_mem(pdata, val, WOL_CTRL);
+
+	val = rd32_mac(pdata, MAC_PMT_STA);
+	fxgmac_set_bits(&val, MAC_PMT_STA_MGKPKTEN_POS,
+			MAC_PMT_STA_MGKPKTEN_LEN, 0);
+	wr32_mac(pdata, val, MAC_PMT_STA);
+}
+
+static void fxgmac_enable_wake_packet_indication(struct fxgmac_pdata *pdata,
+						 int en)
+{
+	u32 val_wpi_crtl0;
+
+	rd32_mem(pdata, MGMT_WOL_CTRL); /* Read-clear WoL event. */
+
+	/* Prepare to write packet data by write wpi_mode to 1 */
+	val_wpi_crtl0 = rd32_mem(pdata, MGMT_WPI_CTRL0);
+	fxgmac_set_bits(&val_wpi_crtl0, MGMT_WPI_CTRL0_WPI_MODE_POS,
+			MGMT_WPI_CTRL0_WPI_MODE_LEN,
+			(en ? MGMT_WPI_CTRL0_WPI_MODE_WR :
+				    MGMT_WPI_CTRL0_WPI_MODE_NORMAL));
+	wr32_mem(pdata, val_wpi_crtl0, MGMT_WPI_CTRL0);
+}
+
+static void fxgmac_enable_wake_link_change(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mem(pdata, WOL_CTRL);
+	fxgmac_set_bits(&val, WOL_LINKCHG_EN_POS, WOL_LINKCHG_EN_LEN, 1);
+	wr32_mem(pdata, val, WOL_CTRL);
+}
+
+static void fxgmac_disable_wake_link_change(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mem(pdata, WOL_CTRL);
+	fxgmac_set_bits(&val, WOL_LINKCHG_EN_POS, WOL_LINKCHG_EN_LEN, 0);
+	wr32_mem(pdata, val, WOL_CTRL);
+}
+
 static void fxgmac_enable_dma_interrupts(struct fxgmac_pdata *pdata)
 {
 	struct fxgmac_channel *channel = pdata->channel_head;
@@ -2686,4 +3251,15 @@ void fxgmac_hw_ops_init(struct fxgmac_hw_ops *hw_ops)
 	hw_ops->set_rss_options = fxgmac_write_rss_options;
 	hw_ops->set_rss_hash_key = fxgmac_set_rss_hash_key;
 	hw_ops->write_rss_lookup_table = fxgmac_write_rss_lookup_table;
+
+	/* Wake */
+	hw_ops->disable_arp_offload = fxgmac_disable_arp_offload;
+	hw_ops->enable_wake_magic_pattern = fxgmac_enable_wake_magic_pattern;
+	hw_ops->disable_wake_magic_pattern = fxgmac_disable_wake_magic_pattern;
+	hw_ops->enable_wake_link_change = fxgmac_enable_wake_link_change;
+	hw_ops->disable_wake_link_change = fxgmac_disable_wake_link_change;
+	hw_ops->set_wake_pattern = fxgmac_set_wake_pattern;
+	hw_ops->enable_wake_pattern = fxgmac_enable_wake_pattern;
+	hw_ops->disable_wake_pattern = fxgmac_disable_wake_pattern;
+
 }
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
index c5c13601e..6a3d1073c 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
@@ -14,6 +14,89 @@
 const struct net_device_ops *fxgmac_get_netdev_ops(void);
 static void fxgmac_napi_enable(struct fxgmac_pdata *pdata);
 
+unsigned int fxgmac_get_netdev_ip4addr(struct fxgmac_pdata *pdata)
+{
+	unsigned int ipval = 0xc0a801ca; /* 192.168.1.202 */
+	struct net_device *netdev = pdata->netdev;
+	struct in_ifaddr *ifa;
+
+	rcu_read_lock();
+
+	/* We only get the first IPv4 addr. */
+	ifa = rcu_dereference(netdev->ip_ptr->ifa_list);
+	if (ifa) {
+		ipval = (unsigned int)ifa->ifa_address;
+		yt_dbg(pdata, "%s, netdev %s IPv4 address %pI4, mask: %pI4\n",
+		       __func__, ifa->ifa_label, &ifa->ifa_address,
+		       &ifa->ifa_mask);
+	}
+
+	rcu_read_unlock();
+
+	return ipval;
+}
+
+unsigned char *fxgmac_get_netdev_ip6addr(struct fxgmac_pdata *pdata,
+					 unsigned char *ipval,
+					 unsigned char *ip6addr_solicited,
+					 unsigned int ifa_flag)
+{
+	struct net_device *netdev = pdata->netdev;
+	unsigned char solicited_ipval[16] = { 0 };
+	unsigned char local_ipval[16] = { 0 };
+	struct in6_addr *addr_ip6_solicited;
+	struct in6_addr *addr_ip6;
+	struct inet6_ifaddr *ifp;
+	int err = -EADDRNOTAVAIL;
+	struct inet6_dev *i6dev;
+
+	if (!(ifa_flag &
+	      (FXGMAC_NS_IFA_GLOBAL_UNICAST | FXGMAC_NS_IFA_LOCAL_LINK))) {
+		yt_err(pdata, "%s, ifa_flag :%d is err.\n", __func__, ifa_flag);
+		return NULL;
+	}
+
+	addr_ip6 = (struct in6_addr *)local_ipval;
+	addr_ip6_solicited = (struct in6_addr *)solicited_ipval;
+
+	if (ipval)
+		addr_ip6 = (struct in6_addr *)ipval;
+
+	if (ip6addr_solicited)
+		addr_ip6_solicited = (struct in6_addr *)ip6addr_solicited;
+
+	in6_pton("fe80::4808:8ffb:d93e:d753", -1, (u8 *)addr_ip6, -1, NULL);
+
+	rcu_read_lock();
+	i6dev = __in6_dev_get(netdev);
+	if (!i6dev)
+		goto err;
+
+	read_lock_bh(&i6dev->lock);
+	list_for_each_entry(ifp, &i6dev->addr_list, if_list) {
+		if (((ifa_flag & FXGMAC_NS_IFA_GLOBAL_UNICAST) &&
+		     ifp->scope != IFA_LINK) ||
+		    ((ifa_flag & FXGMAC_NS_IFA_LOCAL_LINK) &&
+		     ifp->scope == IFA_LINK)) {
+			memcpy(addr_ip6, &ifp->addr, 16);
+			addrconf_addr_solict_mult(addr_ip6, addr_ip6_solicited);
+			err = 0;
+			break;
+		}
+	}
+	read_unlock_bh(&i6dev->lock);
+
+	if (err)
+		goto err;
+
+	rcu_read_unlock();
+	return ipval;
+err:
+	rcu_read_unlock();
+	yt_err(pdata, "%s, get ipv6 addr err, use default.\n", __func__);
+	return NULL;
+}
+
 static unsigned int fxgmac_desc_tx_avail(struct fxgmac_ring *ring)
 {
 	unsigned int avail;
@@ -903,6 +986,41 @@ static void fxgmac_restart_work(struct work_struct *work)
 	rtnl_unlock();
 }
 
+int fxgmac_config_wol(struct fxgmac_pdata *pdata, bool en)
+{
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+
+	if (!pdata->hw_feat.rwk) {
+		yt_err(pdata, "error configuring WOL - not supported.\n");
+		return -EOPNOTSUPP;
+	}
+
+	hw_ops->disable_wake_magic_pattern(pdata);
+	hw_ops->disable_wake_pattern(pdata);
+	hw_ops->disable_wake_link_change(pdata);
+
+	if (en) {
+		/* Config mac address for rx of magic or ucast */
+		hw_ops->set_mac_address(pdata, (u8 *)(pdata->netdev->dev_addr));
+
+		/* Enable Magic packet */
+		if (pdata->wol & WAKE_MAGIC)
+			hw_ops->enable_wake_magic_pattern(pdata);
+
+		/* Enable global unicast packet */
+		if (pdata->wol &
+		    (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_ARP))
+			hw_ops->enable_wake_pattern(pdata);
+
+		/* Enable ephy link change */
+		if (pdata->wol & WAKE_PHY)
+			hw_ops->enable_wake_link_change(pdata);
+	}
+	device_set_wakeup_enable((pdata->dev), en);
+
+	return 0;
+}
+
 int fxgmac_net_powerdown(struct fxgmac_pdata *pdata, bool wake_en)
 {
 	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 15/21] motorcomm:yt6801: Implement pci_driver suspend and resume
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (6 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 14/21] motorcomm:yt6801: Implement the WOL function of ethtool_ops Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 16/21] motorcomm:yt6801: Add a Makefile in the motorcomm folder Frank Sae
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement the pci_driver suspend function to enable the device to sleep,
and implement the resume function to enable the device to resume operation.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 118 ++++++++++++++++++
 .../ethernet/motorcomm/yt6801/yt6801_net.c    |  28 +++++
 .../ethernet/motorcomm/yt6801/yt6801_pci.c    |  61 +++++++++
 3 files changed, 207 insertions(+)

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
index bd3036625..25411f2dd 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
@@ -2424,6 +2424,56 @@ static void fxgmac_config_dma_bus(struct fxgmac_pdata *pdata)
 	wr32_mac(pdata, val, DMA_SBMR);
 }
 
+static int fxgmac_pre_powerdown(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0;
+	int ret;
+
+	fxgmac_disable_rx(pdata);
+
+	yt_dbg(pdata, "%s, phy and mac status update\n", __func__);
+
+	if (device_may_wakeup(pdata->dev)) {
+		val = rd32_mem(pdata, EPHY_CTRL);
+		if (val & EPHY_CTRL_STA_LINKUP) {
+			ret = phy_speed_down(pdata->phydev, true);
+			if (ret < 0) {
+				yt_err(pdata, "%s, phy_speed_down err:%d\n", __func__, ret);
+				return ret;
+			}
+
+			ret = phy_read_status(pdata->phydev);
+			if (ret < 0) {
+				yt_err(pdata, "%s, phy_read_status err:%d\n",
+				       __func__, ret);
+				return ret;
+			}
+
+			pdata->phy_speed = pdata->phydev->speed;
+			pdata->phy_duplex = pdata->phydev->duplex;
+			yt_dbg(pdata, "%s, speed :%d, duplex :%d\n", __func__,
+			       pdata->phy_speed, pdata->phy_duplex);
+
+			fxgmac_config_mac_speed(pdata);
+		} else {
+			yt_dbg(pdata, "%s link down, do nothing\n", __func__);
+		}
+	}
+
+	/* After enable OOB_WOL from efuse, mac will loopcheck phy status,
+	 * and lead to panic sometimes. So we should disable it from powerup,
+	 * enable it from power down.
+	 */
+	val = rd32_mem(pdata, OOB_WOL_CTRL);
+	fxgmac_set_bits(&val, OOB_WOL_CTRL_DIS_POS, OOB_WOL_CTRL_DIS_LEN, 0);
+	wr32_mem(pdata, val, OOB_WOL_CTRL);
+	fsleep(2000);
+
+	fxgmac_set_mac_address(pdata, pdata->mac_addr);
+
+	return 0;
+}
+
 static int fxgmac_phy_clear_interrupt(struct fxgmac_pdata *pdata)
 {
 	u32 stats_pre, stats;
@@ -2458,6 +2508,70 @@ static int fxgmac_phy_clear_interrupt(struct fxgmac_pdata *pdata)
 	return  -ETIMEDOUT;
 }
 
+static void fxgmac_config_powerdown(struct fxgmac_pdata *pdata,
+				    bool wol)
+{
+	u32 val = 0;
+
+	/* Use default arp offloading feature */
+	fxgmac_update_aoe_ipv4addr(pdata, (u8 *)NULL);
+	fxgmac_enable_arp_offload(pdata);
+	fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_GLOBAL_UNICAST);
+	fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_LOCAL_LINK);
+	fxgmac_enable_ns_offload(pdata);
+	fxgmac_enable_wake_packet_indication(pdata, 1);
+
+	/* Enable MAC Rx TX */
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_RE_POS, MAC_CR_RE_LEN, 1);
+	fxgmac_set_bits(&val, MAC_CR_TE_POS, MAC_CR_TE_LEN, 1);
+	wr32_mac(pdata, val, MAC_CR);
+
+	val = rd32_mem(pdata, LPW_CTRL);
+	fxgmac_set_bits(&val, LPW_CTRL_ASPM_LPW_EN_POS,
+			LPW_CTRL_ASPM_LPW_EN_LEN, 1);
+	wr32_mem(pdata, val, LPW_CTRL);
+
+	/* Set gmac power down */
+	val = rd32_mac(pdata, MAC_PMT_STA);
+	fxgmac_set_bits(&val, MAC_PMT_STA_PWRDWN_POS, MAC_PMT_STA_PWRDWN_LEN,
+			1);
+	wr32_mac(pdata, val, MAC_PMT_STA);
+
+	val = rd32_mem(pdata, MGMT_SIGDET);
+	fxgmac_set_bits(&val, MGMT_SIGDET_POS, MGMT_SIGDET_LEN,
+			MGMT_SIGDET_55MV);
+	wr32_mem(pdata, val, MGMT_SIGDET);
+	fxgmac_phy_clear_interrupt(pdata);
+}
+
+static void fxgmac_config_powerup(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0;
+
+	/* After enable OOB_WOL from efuse, mac will loopcheck phy status,
+	 * and lead to panic sometimes.
+	 * So we should disable it from powerup, enable it from power down.
+	 */
+	val = rd32_mem(pdata, OOB_WOL_CTRL);
+	fxgmac_set_bits(&val, OOB_WOL_CTRL_DIS_POS, OOB_WOL_CTRL_DIS_LEN, 1);
+	wr32_mem(pdata, val, OOB_WOL_CTRL);
+
+	/* Clear wpi mode whether or not waked by WOL, write reset value */
+	val = rd32_mem(pdata, MGMT_WPI_CTRL0);
+	fxgmac_set_bits(&val, MGMT_WPI_CTRL0_WPI_MODE_POS,
+			MGMT_WPI_CTRL0_WPI_MODE_LEN, 0);
+	wr32_mem(pdata, val, MGMT_WPI_CTRL0);
+
+	/* Read pmt_status register to De-assert the pmt_intr_o */
+	val = rd32_mac(pdata, MAC_PMT_STA);
+	/* whether or not waked up by WOL, write reset value */
+	fxgmac_set_bits(&val, MAC_PMT_STA_PWRDWN_POS, MAC_PMT_STA_PWRDWN_LEN,
+			0);
+	/* Write register to synchronized always-on block */
+	wr32_mac(pdata, val, MAC_PMT_STA);
+}
+
 #define FXGMAC_WOL_WAIT_2_MS 2
 
 static void fxgmac_config_wol_wait_time(struct fxgmac_pdata *pdata)
@@ -3262,4 +3376,8 @@ void fxgmac_hw_ops_init(struct fxgmac_hw_ops *hw_ops)
 	hw_ops->enable_wake_pattern = fxgmac_enable_wake_pattern;
 	hw_ops->disable_wake_pattern = fxgmac_disable_wake_pattern;
 
+	/* Power Management */
+	hw_ops->pre_power_down = fxgmac_pre_powerdown;
+	hw_ops->config_power_down = fxgmac_config_powerdown;
+	hw_ops->config_power_up = fxgmac_config_powerup;
 }
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
index 6a3d1073c..10c103e95 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
@@ -986,6 +986,34 @@ static void fxgmac_restart_work(struct work_struct *work)
 	rtnl_unlock();
 }
 
+int fxgmac_net_powerup(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+	int ret;
+
+	/* Signal that we are up now */
+	pdata->powerstate = 0;
+	if (__test_and_set_bit(FXGMAC_POWER_STATE_UP, &pdata->powerstate))
+		return 0; /* do nothing if already up */
+
+	ret = fxgmac_start(pdata);
+	if (ret < 0) {
+		yt_err(pdata, "%s: fxgmac_start err: %d\n", __func__, ret);
+		return ret;
+	}
+
+	/* Must call it after fxgmac_start,because it will be
+	 * enable in fxgmac_start
+	 */
+	hw_ops->disable_arp_offload(pdata);
+
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "%s, powerstate :%ld.\n", __func__,
+		       pdata->powerstate);
+
+	return 0;
+}
+
 int fxgmac_config_wol(struct fxgmac_pdata *pdata, bool en)
 {
 	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c
index b2cd75b5c..860b79d13 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c
@@ -104,6 +104,62 @@ static void fxgmac_shutdown(struct pci_dev *pcidev)
 		(system_state == SYSTEM_POWER_OFF) ? 1 : 0);
 }
 
+static int fxgmac_suspend(struct device *device)
+{
+	struct fxgmac_pdata *pdata = dev_get_drvdata(device);
+	struct net_device *netdev = pdata->netdev;
+	int ret = 0;
+
+	mutex_lock(&pdata->mutex);
+	if (pdata->dev_state != FXGMAC_DEV_START)
+		goto unlock;
+
+	if (netif_running(netdev)) {
+		ret = __fxgmac_shutdown(to_pci_dev(device), NULL);
+		if (ret < 0)
+			goto unlock;
+	}
+
+	pdata->dev_state = FXGMAC_DEV_SUSPEND;
+unlock:
+	mutex_unlock(&pdata->mutex);
+
+	return ret;
+}
+
+static int fxgmac_resume(struct device *device)
+{
+	struct fxgmac_pdata *pdata = dev_get_drvdata(device);
+	struct net_device *netdev = pdata->netdev;
+	int ret = 0;
+
+	mutex_lock(&pdata->mutex);
+	if (pdata->dev_state != FXGMAC_DEV_SUSPEND)
+		goto unlock;
+
+	pdata->dev_state = FXGMAC_DEV_RESUME;
+	__clear_bit(FXGMAC_POWER_STATE_DOWN, &pdata->powerstate);
+
+	rtnl_lock();
+	if (netif_running(netdev)) {
+		ret = fxgmac_net_powerup(pdata);
+		if (ret < 0) {
+			dev_err(device, "%s, fxgmac_net_powerup err:%d\n",
+				__func__, ret);
+			goto unlock;
+		}
+	}
+
+	netif_device_attach(netdev);
+	rtnl_unlock();
+
+	dev_dbg(device, "%s ok\n", __func__);
+unlock:
+	mutex_unlock(&pdata->mutex);
+
+	return ret;
+}
+
 #define MOTORCOMM_PCI_ID			0x1f0a
 #define YT6801_PCI_DEVICE_ID			0x6801
 
@@ -114,11 +170,16 @@ static const struct pci_device_id fxgmac_pci_tbl[] = {
 
 MODULE_DEVICE_TABLE(pci, fxgmac_pci_tbl);
 
+static const struct dev_pm_ops fxgmac_pm_ops = {
+	SYSTEM_SLEEP_PM_OPS(fxgmac_suspend, fxgmac_resume)
+};
+
 static struct pci_driver fxgmac_pci_driver = {
 	.name		= FXGMAC_DRV_NAME,
 	.id_table	= fxgmac_pci_tbl,
 	.probe		= fxgmac_probe,
 	.remove		= fxgmac_remove,
+	.driver.pm	= pm_ptr(&fxgmac_pm_ops),
 	.shutdown	= fxgmac_shutdown,
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 16/21] motorcomm:yt6801: Add a Makefile in the motorcomm folder
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (7 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 15/21] motorcomm:yt6801: Implement pci_driver suspend and resume Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 17/21] motorcomm:yt6801: Update the Makefile and Kconfig in the motorcomm Frank Sae
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Add a Makefile in the motorcomm folder to build yt6801 driver.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 drivers/net/ethernet/motorcomm/yt6801/Makefile | 9 +++++++++
 1 file changed, 9 insertions(+)
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/Makefile

diff --git a/drivers/net/ethernet/motorcomm/yt6801/Makefile b/drivers/net/ethernet/motorcomm/yt6801/Makefile
new file mode 100644
index 000000000..72e0acd65
--- /dev/null
+++ b/drivers/net/ethernet/motorcomm/yt6801/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2021 Motor-comm Corporation.
+#
+# Makefile for the Motorcomm(R) 6801 PCI-Express ethernet driver
+#
+
+obj-$(CONFIG_YT6801) += yt6801.o
+yt6801-objs :=  yt6801_desc.o yt6801_ethtool.o yt6801_hw.o \
+		yt6801_net.o  yt6801_pci.o
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 17/21] motorcomm:yt6801: Update the Makefile and Kconfig in the motorcomm
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (8 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 16/21] motorcomm:yt6801: Add a Makefile in the motorcomm folder Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 18/21] motorcomm:yt6801: Update the Makefile and Kconfig in the ethernet Frank Sae
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

1. Add the YT6801 and NET_VENDOR_MOTORCOMM entry in the Kconfig.
2. Add the CONFIG_YT6801 entry in the Makefile.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 drivers/net/ethernet/motorcomm/Kconfig  | 27 +++++++++++++++++++++++++
 drivers/net/ethernet/motorcomm/Makefile |  6 ++++++
 2 files changed, 33 insertions(+)
 create mode 100644 drivers/net/ethernet/motorcomm/Kconfig
 create mode 100644 drivers/net/ethernet/motorcomm/Makefile

diff --git a/drivers/net/ethernet/motorcomm/Kconfig b/drivers/net/ethernet/motorcomm/Kconfig
new file mode 100644
index 000000000..e85d11687
--- /dev/null
+++ b/drivers/net/ethernet/motorcomm/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Motorcomm network device configuration
+#
+
+config NET_VENDOR_MOTORCOMM
+	bool "Motorcomm devices"
+	default y
+	help
+	  If you have a network (Ethernet) device belonging to this class,
+	  say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about Synopsys devices. If you say Y, you will be asked
+	  for your specific device in the following questions.
+
+if NET_VENDOR_MOTORCOMM
+
+config YT6801
+	tristate "Motorcomm(R) 6801 PCI-Express Gigabit Ethernet support"
+	depends on PCI && NET
+	help
+	  This driver supports Motorcomm(R) 6801 gigabit ethernet family of
+	  adapters.
+
+endif # NET_VENDOR_MOTORCOMM
diff --git a/drivers/net/ethernet/motorcomm/Makefile b/drivers/net/ethernet/motorcomm/Makefile
new file mode 100644
index 000000000..511940680
--- /dev/null
+++ b/drivers/net/ethernet/motorcomm/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Motorcomm network device drivers.
+#
+
+obj-$(CONFIG_YT6801) += yt6801/
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 18/21] motorcomm:yt6801: Update the Makefile and Kconfig in the ethernet
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (9 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 17/21] motorcomm:yt6801: Update the Makefile and Kconfig in the motorcomm Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 19/21] ethernet: Update the index.rst in the ethernet documentation folder Frank Sae
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

1. Add the motorcomm entry in the Kconfig.
2. Add the CONFIG_NET_VENDOR_MOTORCOMM entry in the Makefile.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 drivers/net/ethernet/Kconfig  | 1 +
 drivers/net/ethernet/Makefile | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 9a542e3c9..c94f8ee0f 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -127,6 +127,7 @@ source "drivers/net/ethernet/micrel/Kconfig"
 source "drivers/net/ethernet/microchip/Kconfig"
 source "drivers/net/ethernet/mscc/Kconfig"
 source "drivers/net/ethernet/microsoft/Kconfig"
+source "drivers/net/ethernet/motorcomm/Kconfig"
 source "drivers/net/ethernet/moxa/Kconfig"
 source "drivers/net/ethernet/myricom/Kconfig"
 
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 99fa180de..f1f44396f 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_NET_VENDOR_META) += meta/
 obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
 obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
 obj-$(CONFIG_NET_VENDOR_MICROSEMI) += mscc/
+obj-$(CONFIG_NET_VENDOR_MOTORCOMM) += motorcomm/
 obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
 obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
 obj-$(CONFIG_FEALNX) += fealnx.o
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 19/21] ethernet: Update the index.rst in the ethernet documentation folder
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (10 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 18/21] motorcomm:yt6801: Update the Makefile and Kconfig in the ethernet Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 20/21] motorcomm:yt6801: Add a yt6801.rst " Frank Sae
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Add the yt6801 entry in the index.rst.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 Documentation/networking/device_drivers/ethernet/index.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/networking/device_drivers/ethernet/index.rst b/Documentation/networking/device_drivers/ethernet/index.rst
index 6fc196149..f8b88408d 100644
--- a/Documentation/networking/device_drivers/ethernet/index.rst
+++ b/Documentation/networking/device_drivers/ethernet/index.rst
@@ -46,6 +46,7 @@ Contents:
    mellanox/mlx5/index
    meta/fbnic
    microsoft/netvsc
+   motorcomm/yt6801
    neterion/s2io
    netronome/nfp
    pensando/ionic
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 20/21] motorcomm:yt6801: Add a yt6801.rst in the ethernet documentation folder
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (11 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 19/21] ethernet: Update the index.rst in the ethernet documentation folder Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 10:56 ` [PATCH net-next v2 21/21] MAINTAINERS:Add the motorcomm ethernet driver entry Frank Sae
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Add the yt6801.rst in ethernet/motorcomm folder

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../ethernet/motorcomm/yt6801.rst             | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 Documentation/networking/device_drivers/ethernet/motorcomm/yt6801.rst

diff --git a/Documentation/networking/device_drivers/ethernet/motorcomm/yt6801.rst b/Documentation/networking/device_drivers/ethernet/motorcomm/yt6801.rst
new file mode 100644
index 000000000..dd1e59c33
--- /dev/null
+++ b/Documentation/networking/device_drivers/ethernet/motorcomm/yt6801.rst
@@ -0,0 +1,20 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================================================================
+Linux Base Driver for Motorcomm(R) Gigabit PCI Express Adapters
+================================================================
+
+Motorcomm Gigabit Linux driver.
+Copyright (c) 2021 - 2024 Motor-comm Co., Ltd.
+
+
+Contents
+========
+
+- Support
+
+
+Support
+=======
+If you got any problem, contact Motorcomm support team via support@motor-comm.com
+and Cc: netdev.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 21/21] MAINTAINERS:Add the motorcomm ethernet driver entry
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (12 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 20/21] motorcomm:yt6801: Add a yt6801.rst " Frank Sae
@ 2024-11-20 10:56 ` Frank Sae
  2024-11-20 11:14 ` [PATCH net-next v2 11/21] motorcomm:yt6801: Implement some net_device_ops function Frank Sae
  2024-11-20 11:14 ` [PATCH net-next v2 09/21] motorcomm:yt6801: Implement some hw_ops function Frank Sae
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 10:56 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Add myself as the maintainer for the motorcomm ethernet driver.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 96b9344c3..ab63d872d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15686,6 +15686,14 @@ F:	drivers/most/
 F:	drivers/staging/most/
 F:	include/linux/most.h
 
+MOTORCOMM ETHERNET DRIVER
+M:	Frank <Frank.Sae@motor-comm.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+W:	https://www.motor-comm.com/
+F:	Documentation/networking/device_drivers/ethernet/motorcomm/*
+F:	drivers/net/ethernet/motorcomm/*
+
 MOTORCOMM PHY DRIVER
 M:	Frank <Frank.Sae@motor-comm.com>
 L:	netdev@vger.kernel.org
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 11/21] motorcomm:yt6801: Implement some net_device_ops function
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (13 preceding siblings ...)
  2024-11-20 10:56 ` [PATCH net-next v2 21/21] MAINTAINERS:Add the motorcomm ethernet driver entry Frank Sae
@ 2024-11-20 11:14 ` Frank Sae
  2024-11-20 11:14 ` [PATCH net-next v2 09/21] motorcomm:yt6801: Implement some hw_ops function Frank Sae
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 11:14 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement following callback function
.ndo_stop
.ndo_start_xmit
.ndo_get_stats64
.ndo_set_mac_address
.ndo_validate_addr
.ndo_vlan_rx_add_vid
.ndo_vlan_rx_kill_vid
.ndo_poll_controller
.ndo_set_features
.ndo_fix_features
.ndo_set_rx_mode

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../ethernet/motorcomm/yt6801/yt6801_net.c    | 196 ++++++++++++++++++
 1 file changed, 196 insertions(+)

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
index fa1587e69..ed65c9cc9 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
@@ -996,6 +996,25 @@ static int fxgmac_open(struct net_device *netdev)
 	return ret;
 }
 
+static int fxgmac_close(struct net_device *netdev)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+
+	mutex_lock(&pdata->mutex);
+	fxgmac_stop(pdata);		/* Stop the device */
+	pdata->dev_state = FXGMAC_DEV_CLOSE;
+	fxgmac_channels_rings_free(pdata); /* Free the channels and rings */
+	hw_ops->reset_phy(pdata);
+	phy_disconnect(pdata->phydev);
+	mutex_unlock(&pdata->mutex);
+
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "%s ok\n", __func__);
+
+	return 0;
+}
+
 #define EFUSE_FISRT_UPDATE_ADDR				255
 #define EFUSE_SECOND_UPDATE_ADDR			209
 #define EFUSE_MAX_ENTRY					39
@@ -1981,9 +2000,186 @@ static netdev_tx_t fxgmac_xmit(struct sk_buff *skb, struct net_device *netdev)
 
 	return NETDEV_TX_OK;
 }
+
+static void fxgmac_get_stats64(struct net_device *netdev,
+			       struct rtnl_link_stats64 *s)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_stats *pstats = &pdata->stats;
+
+	if (test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->powerstate))
+		return;
+
+	pdata->hw_ops.read_mmc_stats(pdata);
+
+	s->rx_packets = pstats->rxframecount_gb;
+	s->rx_bytes = pstats->rxoctetcount_gb;
+	s->rx_errors = pstats->rxframecount_gb - pstats->rxbroadcastframes_g -
+		       pstats->rxmulticastframes_g - pstats->rxunicastframes_g;
+
+	s->rx_length_errors = pstats->rxlengtherror;
+	s->rx_crc_errors = pstats->rxcrcerror;
+	s->rx_fifo_errors = pstats->rxfifooverflow;
+
+	s->tx_packets = pstats->txframecount_gb;
+	s->tx_bytes = pstats->txoctetcount_gb;
+	s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g;
+	s->tx_dropped = netdev->stats.tx_dropped;
+}
+
+static int fxgmac_set_mac_address(struct net_device *netdev, void *addr)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+	struct sockaddr *saddr = addr;
+
+	if (!is_valid_ether_addr(saddr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	eth_hw_addr_set(netdev, saddr->sa_data);
+	memcpy(pdata->mac_addr, saddr->sa_data, netdev->addr_len);
+	hw_ops->set_mac_address(pdata, saddr->sa_data);
+	hw_ops->set_mac_hash(pdata);
+
+	yt_dbg(pdata, "fxgmac,set mac addr to %pM\n", netdev->dev_addr);
+
+	return 0;
+}
+
+static int fxgmac_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
+				  u16 vid)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+
+	set_bit(vid, pdata->active_vlans);
+	hw_ops->update_vlan_hash_table(pdata);
+
+	yt_dbg(pdata, "fxgmac,add rx vlan %d\n", vid);
+
+	return 0;
+}
+
+static int fxgmac_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
+				   u16 vid)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+
+	clear_bit(vid, pdata->active_vlans);
+	hw_ops->update_vlan_hash_table(pdata);
+
+	yt_dbg(pdata, "fxgmac,del rx vlan %d\n", vid);
+
+	return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void fxgmac_poll_controller(struct net_device *netdev)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_channel *channel;
+
+	if (pdata->per_channel_irq) {
+		channel = pdata->channel_head;
+		for (u32 i = 0; i < pdata->channel_count; i++, channel++)
+			fxgmac_dma_isr(channel->dma_irq_rx, channel);
+	} else {
+		disable_irq(pdata->dev_irq);
+		fxgmac_isr(pdata->dev_irq, pdata);
+		enable_irq(pdata->dev_irq);
+	}
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+static netdev_features_t fxgmac_fix_features(struct net_device *netdev,
+					     netdev_features_t features)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	u32 fifo_size;
+
+	fifo_size = pdata->hw_ops.calculate_max_checksum_size(pdata);
+	if (netdev->mtu > fifo_size) {
+		features &= ~NETIF_F_IP_CSUM;
+		features &= ~NETIF_F_IPV6_CSUM;
+	}
+
+	return features;
+}
+
+static int fxgmac_set_features(struct net_device *netdev,
+			       netdev_features_t features)
+{
+	netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter, tso;
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_hw_ops *hw_ops;
+
+	hw_ops = &pdata->hw_ops;
+	rxhash = pdata->netdev_features & NETIF_F_RXHASH;
+	rxcsum = pdata->netdev_features & NETIF_F_RXCSUM;
+	rxvlan = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX;
+	rxvlan_filter = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER;
+	tso = pdata->netdev_features & (NETIF_F_TSO | NETIF_F_TSO6);
+
+	if ((features & (NETIF_F_TSO | NETIF_F_TSO6)) && !tso) {
+		yt_dbg(pdata, "enable tso.\n");
+		pdata->hw_feat.tso = 1;
+		hw_ops->config_tso(pdata);
+	} else if (!(features & (NETIF_F_TSO | NETIF_F_TSO6)) && tso) {
+		yt_dbg(pdata, "disable tso.\n");
+		pdata->hw_feat.tso = 0;
+		hw_ops->config_tso(pdata);
+	}
+
+	if ((features & NETIF_F_RXHASH) && !rxhash)
+		hw_ops->enable_rss(pdata);
+	else if (!(features & NETIF_F_RXHASH) && rxhash)
+		hw_ops->disable_rss(pdata);
+
+	if ((features & NETIF_F_RXCSUM) && !rxcsum)
+		hw_ops->enable_rx_csum(pdata);
+	else if (!(features & NETIF_F_RXCSUM) && rxcsum)
+		hw_ops->disable_rx_csum(pdata);
+
+	if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan)
+		hw_ops->enable_rx_vlan_stripping(pdata);
+	else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan)
+		hw_ops->disable_rx_vlan_stripping(pdata);
+
+	if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && !rxvlan_filter)
+		hw_ops->enable_rx_vlan_filtering(pdata);
+	else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter)
+		hw_ops->disable_rx_vlan_filtering(pdata);
+
+	pdata->netdev_features = features;
+
+	yt_dbg(pdata, "fxgmac,set features done,%llx\n", (u64)features);
+	return 0;
+}
+
+static void fxgmac_set_rx_mode(struct net_device *netdev)
+{
+	struct fxgmac_pdata *pdata = netdev_priv(netdev);
+	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
+
+	hw_ops->config_rx_mode(pdata);
+}
+
 static const struct net_device_ops fxgmac_netdev_ops = {
 	.ndo_open		= fxgmac_open,
+	.ndo_stop		= fxgmac_close,
 	.ndo_start_xmit		= fxgmac_xmit,
+	.ndo_get_stats64	= fxgmac_get_stats64,
+	.ndo_set_mac_address	= fxgmac_set_mac_address,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_vlan_rx_add_vid	= fxgmac_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= fxgmac_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= fxgmac_poll_controller,
+#endif
+	.ndo_set_features	= fxgmac_set_features,
+	.ndo_fix_features	= fxgmac_fix_features,
+	.ndo_set_rx_mode	= fxgmac_set_rx_mode,
 };
 
 const struct net_device_ops *fxgmac_get_netdev_ops(void)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH net-next v2 09/21] motorcomm:yt6801: Implement some hw_ops function
  2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
                   ` (14 preceding siblings ...)
  2024-11-20 11:14 ` [PATCH net-next v2 11/21] motorcomm:yt6801: Implement some net_device_ops function Frank Sae
@ 2024-11-20 11:14 ` Frank Sae
  15 siblings, 0 replies; 17+ messages in thread
From: Frank Sae @ 2024-11-20 11:14 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni
  Cc: netdev, linux-kernel, xiaogang.fan, fei.zhang, hua.sun, Frank.Sae

Implement some hw_ops function to set default hardware settings, including
PHY control, Vlan related config, RX coalescing, Receive Side Scaling and
other basic function control.

Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
---
 .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 2216 +++++++++++++++++
 1 file changed, 2216 insertions(+)
 create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c

diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
new file mode 100644
index 000000000..1af26b0b4
--- /dev/null
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c
@@ -0,0 +1,2216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2022 - 2024 Motorcomm Electronic Technology Co.,Ltd. */
+
+#include "yt6801_desc.h"
+#include "yt6801_net.h"
+
+static void fxgmac_disable_rx_csum(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_IPC_POS, MAC_CR_IPC_LEN, 0);
+	wr32_mac(pdata, val, MAC_CR);
+}
+
+static void fxgmac_enable_rx_csum(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_IPC_POS, MAC_CR_IPC_LEN, 1);
+	wr32_mac(pdata, val, MAC_CR);
+}
+
+static void fxgmac_disable_tx_vlan(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_VLANIR);
+	/* Set VLAN Tag input enable */
+	fxgmac_set_bits(&val, MAC_VLANIR_CSVL_POS, MAC_VLANIR_CSVL_LEN, 0);
+	fxgmac_set_bits(&val, MAC_VLANIR_VLTI_POS, MAC_VLANIR_VLTI_LEN, 1);
+	/* Set VLAN priority control disable */
+	fxgmac_set_bits(&val, MAC_VLANIR_VLP_POS, MAC_VLANIR_VLP_LEN, 0);
+	fxgmac_set_bits(&val, MAC_VLANIR_VLC_POS, MAC_VLANIR_VLC_LEN, 0);
+	wr32_mac(pdata, val, MAC_VLANIR);
+}
+
+static void fxgmac_enable_rx_vlan_stripping(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_VLANTR);
+	/* Put the VLAN tag in the Rx descriptor */
+	fxgmac_set_bits(&val, MAC_VLANTR_EVLRXS_POS, MAC_VLANTR_EVLRXS_LEN, 1);
+	/* Don't check the VLAN type */
+	fxgmac_set_bits(&val, MAC_VLANTR_DOVLTC_POS, MAC_VLANTR_DOVLTC_LEN, 1);
+	/* Check only C-TAG (0x8100) packets */
+	fxgmac_set_bits(&val, MAC_VLANTR_ERSVLM_POS, MAC_VLANTR_ERSVLM_LEN, 0);
+	/* Don't consider an S-TAG (0x88A8) packet as a VLAN packet */
+	fxgmac_set_bits(&val, MAC_VLANTR_ESVL_POS, MAC_VLANTR_ESVL_LEN, 0);
+	/* Enable VLAN tag stripping */
+	fxgmac_set_bits(&val, MAC_VLANTR_EVLS_POS, MAC_VLANTR_EVLS_LEN, 3);
+	wr32_mac(pdata, val, MAC_VLANTR);
+}
+
+static void fxgmac_disable_rx_vlan_stripping(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_VLANTR);
+	fxgmac_set_bits(&val, MAC_VLANTR_EVLS_POS, MAC_VLANTR_EVLS_LEN, 0);
+	wr32_mac(pdata, val, MAC_VLANTR);
+}
+
+static void fxgmac_enable_rx_vlan_filtering(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_PFR);
+	/* Enable VLAN filtering */
+	fxgmac_set_bits(&val, MAC_PFR_VTFE_POS, MAC_PFR_VTFE_LEN, 1);
+	wr32_mac(pdata, val, MAC_PFR);
+
+	val = rd32_mac(pdata, MAC_VLANTR);
+	/* Enable VLAN Hash Table filtering */
+	fxgmac_set_bits(&val, MAC_VLANTR_VTHM_POS, MAC_VLANTR_VTHM_LEN, 1);
+	/* Disable VLAN tag inverse matching */
+	fxgmac_set_bits(&val, MAC_VLANTR_VTIM_POS, MAC_VLANTR_VTIM_LEN, 0);
+	/* Only filter on the lower 12-bits of the VLAN tag */
+	fxgmac_set_bits(&val, MAC_VLANTR_ETV_POS, MAC_VLANTR_ETV_LEN, 1);
+}
+
+static void fxgmac_disable_rx_vlan_filtering(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_PFR);
+	fxgmac_set_bits(&val, MAC_PFR_VTFE_POS, MAC_PFR_VTFE_LEN, 0);
+	wr32_mac(pdata, val, MAC_PFR);
+}
+
+static u32 fxgmac_vid_crc32_le(__le16 vid_le)
+{
+	unsigned char *data = (unsigned char *)&vid_le;
+	unsigned char data_byte = 0;
+	u32 crc = ~0;
+	u32 temp = 0;
+	int i, bits;
+
+	bits = get_bitmask_order(VLAN_VID_MASK);
+	for (i = 0; i < bits; i++) {
+		if ((i % 8) == 0)
+			data_byte = data[i / 8];
+
+		temp = ((crc & 1) ^ data_byte) & 1;
+		crc >>= 1;
+		data_byte >>= 1;
+
+		if (temp)
+			crc ^= CRC32_POLY_LE;
+	}
+
+	return crc;
+}
+
+static void fxgmac_update_vlan_hash_table(struct fxgmac_pdata *pdata)
+{
+	u16 vid, vlan_hash_table = 0;
+	__le16 vid_le;
+	u32 val, crc;
+
+	/* Generate the VLAN Hash Table value */
+	for_each_set_bit(vid, pdata->active_vlans, VLAN_N_VID) {
+		/* Get the CRC32 value of the VLAN ID */
+		vid_le = cpu_to_le16(vid);
+		crc = bitrev32(~fxgmac_vid_crc32_le(vid_le)) >> 28;
+
+		vlan_hash_table |= (1 << crc);
+	}
+
+	/* Set the VLAN Hash Table filtering register */
+	val = rd32_mac(pdata, MAC_VLANHTR);
+	fxgmac_set_bits(&val, MAC_VLANHTR_VLHT_POS, MAC_VLANHTR_VLHT_LEN,
+			vlan_hash_table);
+	wr32_mac(pdata, val, MAC_VLANHTR);
+
+	yt_dbg(pdata, "fxgmac_update_vlan_hash_tabl done,hash tbl=%08x.\n",
+	       vlan_hash_table);
+}
+
+static void fxgmac_set_promiscuous_mode(struct fxgmac_pdata *pdata,
+					unsigned int enable)
+{
+	u32 val, en = enable ? 1 : 0;
+
+	val = rd32_mac(pdata, MAC_PFR);
+	if (FXGMAC_GET_BITS(val, MAC_PFR_PR_POS, MAC_PFR_PR_LEN) == en)
+		return;
+
+	fxgmac_set_bits(&val, MAC_PFR_PR_POS, MAC_PFR_PR_LEN, en);
+	wr32_mac(pdata, val, MAC_PFR);
+
+	/* Hardware will still perform VLAN filtering in promiscuous mode */
+	if (enable) {
+		fxgmac_disable_rx_vlan_filtering(pdata);
+		return;
+	}
+
+	if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+		fxgmac_enable_rx_vlan_filtering(pdata);
+}
+
+static void fxgmac_enable_rx_broadcast(struct fxgmac_pdata *pdata,
+				       unsigned int enable)
+{
+	u32 val, en = enable ? 0 : 1;
+
+	val = rd32_mac(pdata, MAC_PFR);
+	if (FXGMAC_GET_BITS(val, MAC_PFR_DBF_POS, MAC_PFR_DBF_LEN) == en)
+		return;
+
+	fxgmac_set_bits(&val, MAC_PFR_DBF_POS, MAC_PFR_DBF_LEN, en);
+	wr32_mac(pdata, val, MAC_PFR);
+}
+
+static void fxgmac_set_all_multicast_mode(struct fxgmac_pdata *pdata,
+					  unsigned int enable)
+{
+	u32 val, en = enable ? 1 : 0;
+
+	val = rd32_mac(pdata, MAC_PFR);
+	if (FXGMAC_GET_BITS(val, MAC_PFR_PM_POS, MAC_PFR_PM_LEN) == en)
+		return;
+
+	fxgmac_set_bits(&val, MAC_PFR_PM_POS, MAC_PFR_PM_LEN, en);
+	wr32_mac(pdata, val, MAC_PFR);
+}
+
+static void fxgmac_set_mac_reg(struct fxgmac_pdata *pdata,
+			       struct netdev_hw_addr *ha, unsigned int *mac_reg)
+{
+	unsigned int mac_hi, mac_lo;
+	u8 *mac_addr;
+
+	mac_lo = 0;
+	mac_hi = 0;
+
+	if (ha) {
+		mac_addr = (u8 *)&mac_lo;
+		mac_addr[0] = ha->addr[0];
+		mac_addr[1] = ha->addr[1];
+		mac_addr[2] = ha->addr[2];
+		mac_addr[3] = ha->addr[3];
+
+		mac_addr = (u8 *)&mac_hi;
+		mac_addr[0] = ha->addr[4];
+		mac_addr[1] = ha->addr[5];
+
+		fxgmac_set_bits(&mac_hi, MAC_MACA1HR_AE_POS,
+				MAC_MACA1HR_AE_LEN, 1);
+
+		yt_dbg(pdata, "adding mac address %pM at %#x\n", ha->addr,
+		       *mac_reg);
+	}
+
+	/* If "ha" is NULL, clear the additional MAC address entries */
+	wr32_mac(pdata, mac_hi, *mac_reg);
+	*mac_reg += MAC_MACA_INC;
+	wr32_mac(pdata, mac_lo, *mac_reg);
+	*mac_reg += MAC_MACA_INC;
+}
+
+static void fxgmac_set_mac_addn_addrs(struct fxgmac_pdata *pdata)
+{
+	struct net_device *netdev = pdata->netdev;
+	unsigned int addn_macs, mac_reg;
+	struct netdev_hw_addr *ha;
+
+	mac_reg = MAC_MACA1HR;
+	addn_macs = pdata->hw_feat.addn_mac;
+
+	if (netdev_uc_count(netdev) > addn_macs) {
+		fxgmac_set_promiscuous_mode(pdata, 1);
+	} else {
+		netdev_for_each_uc_addr(ha, netdev) {
+			fxgmac_set_mac_reg(pdata, ha, &mac_reg);
+			addn_macs--;
+		}
+
+		if (netdev_mc_count(netdev) > addn_macs) {
+			fxgmac_set_all_multicast_mode(pdata, 1);
+		} else {
+			netdev_for_each_mc_addr(ha, netdev) {
+				fxgmac_set_mac_reg(pdata, ha, &mac_reg);
+				addn_macs--;
+			}
+		}
+	}
+
+	/* Clear remaining additional MAC address entries */
+	while (addn_macs--)
+		fxgmac_set_mac_reg(pdata, NULL, &mac_reg);
+}
+
+#define GET_REG_AND_BIT_POS(reversalval, regout, bitout)                       \
+	do {                                                                   \
+		typeof(reversalval)(_reversalval) = (reversalval);             \
+		regout = (((_reversalval) >> 5) & 0x7);                        \
+		bitout = ((_reversalval) & 0x1f);                              \
+	} while (0)
+
+static u32 fxgmac_crc32(unsigned char *data, int length)
+{
+	u32 crc = ~0;
+
+	while (--length >= 0) {
+		unsigned char byte = *data++;
+		int bit;
+
+		for (bit = 8; --bit >= 0; byte >>= 1) {
+			if ((crc ^ byte) & 1) {
+				crc >>= 1;
+				crc ^= CRC32_POLY_LE;
+			} else {
+				crc >>= 1;
+			}
+		}
+	}
+
+	return ~crc;
+}
+
+/* Maximum MAC address hash table size (256 bits = 8 bytes) */
+#define FXGMAC_MAC_HASH_TABLE_SIZE 8
+
+static void fxgmac_config_multicast_mac_hash_table(struct fxgmac_pdata *pdata,
+						   unsigned char *pmc_mac,
+						   int b_add)
+{
+	unsigned int j, hash_reg, reg_bit;
+	u32 val, crc, reversal_crc;
+
+	if (!pmc_mac) {
+		for (j = 0; j < FXGMAC_MAC_HASH_TABLE_SIZE; j++) {
+			hash_reg = j;
+			hash_reg = (MAC_HTR0 + hash_reg * MAC_HTR_INC);
+			wr32_mac(pdata, 0, hash_reg);
+		}
+		return;
+	}
+
+	crc = fxgmac_crc32(pmc_mac, ETH_ALEN);
+
+	/* Reverse the crc */
+	for (j = 0, reversal_crc = 0; j < 32; j++) {
+		if (crc & ((u32)1 << j))
+			reversal_crc |= 1 << (31 - j);
+	}
+
+	GET_REG_AND_BIT_POS((reversal_crc >> 24), hash_reg, reg_bit);
+	/* Set the MAC Hash Table registers */
+	hash_reg = (MAC_HTR0 + hash_reg * MAC_HTR_INC);
+
+	val = rd32_mac(pdata, hash_reg);
+	fxgmac_set_bits(&val, reg_bit, 1, (b_add ? 1 : 0));
+	wr32_mac(pdata, val, hash_reg);
+}
+
+static void fxgmac_set_mac_hash_table(struct fxgmac_pdata *pdata)
+{
+	struct net_device *netdev = pdata->netdev;
+	struct netdev_hw_addr *ha;
+
+	fxgmac_config_multicast_mac_hash_table(pdata, NULL, 1);
+	netdev_for_each_mc_addr(ha, netdev) {
+		fxgmac_config_multicast_mac_hash_table(pdata, ha->addr, 1);
+	}
+}
+
+static void fxgmac_set_mc_addresses(struct fxgmac_pdata *pdata)
+{
+	if (pdata->hw_feat.hash_table_size)
+		fxgmac_set_mac_hash_table(pdata);
+	else
+		fxgmac_set_mac_addn_addrs(pdata);
+}
+
+static void fxgmac_set_multicast_mode(struct fxgmac_pdata *pdata,
+				      unsigned int enable)
+{
+	if (enable)
+		fxgmac_set_mc_addresses(pdata);
+	else
+		fxgmac_config_multicast_mac_hash_table(pdata, NULL, 1);
+}
+
+static void fxgmac_set_mac_address(struct fxgmac_pdata *pdata, u8 *addr)
+{
+	u32 mac_hi, mac_lo;
+
+	mac_lo = (u32)addr[0] | ((u32)addr[1] << 8) |
+		  ((u32)addr[2] << 16) | ((u32)addr[3] << 24);
+
+	mac_hi = (u32)addr[4] | ((u32)addr[5] << 8);
+
+	wr32_mac(pdata, mac_lo, MAC_MACA0LR);
+	wr32_mac(pdata, mac_hi, MAC_MACA0HR);
+}
+
+static void fxgmac_config_mac_address(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	fxgmac_set_mac_address(pdata, pdata->mac_addr);
+
+	val = rd32_mac(pdata, MAC_PFR);
+	fxgmac_set_bits(&val, MAC_PFR_HPF_POS, MAC_PFR_HPF_LEN, 1);
+	fxgmac_set_bits(&val, MAC_PFR_HUC_POS, MAC_PFR_HUC_LEN, 1);
+	fxgmac_set_bits(&val, MAC_PFR_HMC_POS, MAC_PFR_HMC_LEN, 1);
+	wr32_mac(pdata, val, MAC_PFR);
+}
+
+static void fxgmac_config_crc_check(struct fxgmac_pdata *pdata)
+{
+	u32 val, en = pdata->crc_check ? 0 : 1;
+
+	val = rd32_mac(pdata, MAC_ECR);
+	fxgmac_set_bits(&val, MAC_ECR_DCRCC_POS, MAC_ECR_DCRCC_LEN, en);
+	wr32_mac(pdata, val, MAC_ECR);
+}
+
+static void fxgmac_config_jumbo(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_JE_POS, MAC_CR_JE_LEN, pdata->jumbo);
+	wr32_mac(pdata, val, MAC_CR);
+}
+
+static void fxgmac_config_checksum_offload(struct fxgmac_pdata *pdata)
+{
+	if (pdata->netdev->features & NETIF_F_RXCSUM)
+		fxgmac_enable_rx_csum(pdata);
+	else
+		fxgmac_disable_rx_csum(pdata);
+}
+
+static void fxgmac_config_vlan_support(struct fxgmac_pdata *pdata)
+{
+	/*  Configure dynamical vlanID from TX Context. */
+	fxgmac_disable_tx_vlan(pdata);
+
+	/* Set the current VLAN Hash Table register value */
+	fxgmac_update_vlan_hash_table(pdata);
+
+	if (pdata->vlan_filter)
+		fxgmac_enable_rx_vlan_filtering(pdata);
+	else
+		fxgmac_disable_rx_vlan_filtering(pdata);
+
+	if (pdata->vlan_strip)
+		fxgmac_enable_rx_vlan_stripping(pdata);
+	else
+		fxgmac_disable_rx_vlan_stripping(pdata);
+}
+
+static void fxgmac_config_rx_mode(struct fxgmac_pdata *pdata)
+{
+	u32 pr_mode, am_mode, mu_mode, bd_mode;
+
+	pr_mode = ((pdata->netdev->flags & IFF_PROMISC) != 0);
+	am_mode = ((pdata->netdev->flags & IFF_ALLMULTI) != 0);
+	mu_mode = ((pdata->netdev->flags & IFF_MULTICAST) != 0);
+	bd_mode = ((pdata->netdev->flags & IFF_BROADCAST) != 0);
+
+	fxgmac_enable_rx_broadcast(pdata, bd_mode);
+	fxgmac_set_promiscuous_mode(pdata, pr_mode);
+	fxgmac_set_all_multicast_mode(pdata, am_mode);
+	fxgmac_set_multicast_mode(pdata, mu_mode);
+}
+
+static void fxgmac_prepare_tx_stop(struct fxgmac_pdata *pdata,
+				   struct fxgmac_channel *channel)
+{
+	unsigned int tx_q_idx, tx_status;
+	unsigned int tx_dsr, tx_pos;
+	unsigned long tx_timeout;
+
+	/* Calculate the status register to read and the position within */
+	if (channel->queue_index < DMA_DSRX_FIRST_QUEUE) {
+		tx_dsr = DMA_DSR0;
+		tx_pos = (channel->queue_index * DMA_DSR_Q_LEN) +
+			 DMA_DSR0_TPS_START;
+	} else {
+		tx_q_idx = channel->queue_index - DMA_DSRX_FIRST_QUEUE;
+
+		tx_dsr = DMA_DSR1 + ((tx_q_idx / DMA_DSRX_QPR) * DMA_DSRX_INC);
+		tx_pos = ((tx_q_idx % DMA_DSRX_QPR) * DMA_DSR_Q_LEN) +
+			 DMA_DSRX_TPS_START;
+	}
+
+	/* The Tx engine cannot be stopped if it is actively processing
+	 * descriptors. Wait for the Tx engine to enter the stopped or
+	 * suspended state.
+	 */
+	tx_timeout = jiffies + (FXGMAC_DMA_STOP_TIMEOUT * HZ);
+
+	while (time_before(jiffies, tx_timeout)) {
+		tx_status = rd32_mac(pdata, tx_dsr);
+		tx_status = FXGMAC_GET_BITS(tx_status, tx_pos, DMA_DSR_TPS_LEN);
+		if (tx_status == DMA_TPS_STOPPED ||
+		    tx_status == DMA_TPS_SUSPENDED)
+			break;
+
+		fsleep(500);
+	}
+
+	if (!time_before(jiffies, tx_timeout))
+		yt_dbg(pdata,
+		       "timed out waiting for Tx DMA channel %u to stop\n",
+		       channel->queue_index);
+}
+
+static void fxgmac_enable_tx(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	/* Enable Tx DMA channel */
+	val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+	fxgmac_set_bits(&val, DMA_CH_TCR_ST_POS, DMA_CH_TCR_ST_LEN, 1);
+	wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+
+	/* Enable Tx queue */
+	val = rd32_mac(pdata, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+	fxgmac_set_bits(&val, MTL_Q_TQOMR_TXQEN_POS,
+			MTL_Q_TQOMR_TXQEN_LEN, MTL_Q_ENABLED);
+	wr32_mac(pdata, val, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+
+	/* Enable MAC Tx */
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_TE_POS, MAC_CR_TE_LEN, 1);
+	wr32_mac(pdata, val, MAC_CR);
+}
+
+static void fxgmac_disable_tx(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	/* Prepare for Tx DMA channel stop */
+	fxgmac_prepare_tx_stop(pdata, channel);
+
+	/* Disable MAC Tx */
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_TE_POS, MAC_CR_TE_LEN, 0);
+	wr32_mac(pdata, val, MAC_CR);
+
+	/* Disable Tx queue */
+	val = rd32_mac(pdata, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+	fxgmac_set_bits(&val, MTL_Q_TQOMR_TXQEN_POS,
+			MTL_Q_TQOMR_TXQEN_LEN, 0);
+	wr32_mac(pdata, val, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+
+	/* Disable Tx DMA channel */
+	val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+	fxgmac_set_bits(&val, DMA_CH_TCR_ST_POS,
+			DMA_CH_TCR_ST_LEN, 0);
+	wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+}
+
+static void fxgmac_prepare_rx_stop(struct fxgmac_pdata *pdata,
+				   unsigned int queue)
+{
+	unsigned int rx_status, rx_q, rx_q_sts;
+	unsigned long rx_timeout;
+
+	/* The Rx engine cannot be stopped if it is actively processing
+	 * packets. Wait for the Rx queue to empty the Rx fifo.
+	 */
+	rx_timeout = jiffies + (FXGMAC_DMA_STOP_TIMEOUT * HZ);
+
+	while (time_before(jiffies, rx_timeout)) {
+		rx_status = rd32_mac(pdata, FXGMAC_MTL_REG(queue, MTL_Q_RQDR));
+		rx_q = FXGMAC_GET_BITS(rx_status, MTL_Q_RQDR_PRXQ_POS,
+				       MTL_Q_RQDR_PRXQ_LEN);
+		rx_q_sts = FXGMAC_GET_BITS(rx_status, MTL_Q_RQDR_RXQSTS_POS,
+					   MTL_Q_RQDR_RXQSTS_LEN);
+		if (rx_q == 0 && rx_q_sts == 0)
+			break;
+
+		fsleep(500);
+	}
+
+	if (!time_before(jiffies, rx_timeout))
+		yt_dbg(pdata, "timed out waiting for Rx queue %u to empty\n",
+		       queue);
+}
+
+static void fxgmac_enable_rx(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val, i;
+
+	/* Enable each Rx DMA channel */
+	for (i = 0; i < pdata->channel_count; i++, channel++) {
+		val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
+		fxgmac_set_bits(&val, DMA_CH_RCR_SR_POS, DMA_CH_RCR_SR_LEN, 1);
+		wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
+	}
+
+	/* Enable each Rx queue */
+	val = 0;
+	for (i = 0; i < pdata->rx_q_count; i++)
+		val |= (0x02 << (i << 1));
+
+	wr32_mac(pdata, val, MAC_RQC0R);
+
+	/* Enable MAC Rx */
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_CST_POS, MAC_CR_CST_LEN, 1);
+	fxgmac_set_bits(&val, MAC_CR_ACS_POS, MAC_CR_ACS_LEN, 1);
+	fxgmac_set_bits(&val, MAC_CR_RE_POS, MAC_CR_RE_LEN, 1);
+	wr32_mac(pdata, val, MAC_CR);
+}
+
+static void fxgmac_disable_rx(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val, i;
+
+	/* Disable MAC Rx */
+	val = rd32_mac(pdata, MAC_CR);
+	fxgmac_set_bits(&val, MAC_CR_CST_POS, MAC_CR_CST_LEN, 0);
+	fxgmac_set_bits(&val, MAC_CR_ACS_POS, MAC_CR_ACS_LEN, 0);
+	fxgmac_set_bits(&val, MAC_CR_RE_POS, MAC_CR_RE_LEN, 0);
+	wr32_mac(pdata, val, MAC_CR);
+
+	/* Prepare for Rx DMA channel stop */
+	for (i = 0; i < pdata->rx_q_count; i++)
+		fxgmac_prepare_rx_stop(pdata, i);
+
+	wr32_mac(pdata, 0, MAC_RQC0R); /* Disable each Rx queue */
+
+	/* Disable each Rx DMA channel */
+	for (i = 0; i < pdata->channel_count; i++, channel++) {
+		val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
+		fxgmac_set_bits(&val, DMA_CH_RCR_SR_POS, DMA_CH_RCR_SR_LEN, 0);
+		wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
+	}
+}
+
+static void fxgmac_config_tx_flow_control(struct fxgmac_pdata *pdata)
+{
+	u32 val, i;
+
+	/* Set MTL flow control */
+	for (i = 0; i < pdata->rx_q_count; i++) {
+		val = rd32_mac(pdata, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+		fxgmac_set_bits(&val, MTL_Q_RQOMR_EHFC_POS,
+				MTL_Q_RQOMR_EHFC_LEN, pdata->tx_pause);
+		wr32_mac(pdata, val, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+	}
+
+	/* Set MAC flow control */
+	val = rd32_mac(pdata, MAC_Q0TFCR);
+	fxgmac_set_bits(&val, MAC_Q0TFCR_TFE_POS, MAC_Q0TFCR_TFE_LEN,
+			pdata->tx_pause);
+	if (pdata->tx_pause == 1)
+		/* Set pause time */
+		fxgmac_set_bits(&val, MAC_Q0TFCR_PT_POS,
+				MAC_Q0TFCR_PT_LEN, 0xffff);
+	wr32_mac(pdata, val, MAC_Q0TFCR);
+}
+
+static void fxgmac_config_rx_flow_control(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_RFCR);
+	fxgmac_set_bits(&val, MAC_RFCR_RFE_POS, MAC_RFCR_RFE_LEN,
+			pdata->rx_pause);
+	wr32_mac(pdata, val, MAC_RFCR);
+}
+
+static void fxgmac_config_rx_coalesce(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		if (!channel->rx_ring)
+			break;
+
+		val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_RIWT));
+		fxgmac_set_bits(&val, DMA_CH_RIWT_RWT_POS, DMA_CH_RIWT_RWT_LEN,
+				pdata->rx_riwt);
+		wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_RIWT));
+	}
+}
+
+static void fxgmac_config_rx_fep_disable(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	for (u32 i = 0; i < pdata->rx_q_count; i++) {
+		val = rd32_mac(pdata, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+		/* Enable the rx queue forward packet with error status
+		 * (crc error,gmii_er, watch dog timeout.or overflow)
+		 */
+		fxgmac_set_bits(&val, MTL_Q_RQOMR_FEP_POS, MTL_Q_RQOMR_FEP_LEN,
+				MTL_FEP_ENABLE);
+		wr32_mac(pdata, val, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+	}
+}
+
+static void fxgmac_config_rx_fup_enable(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	for (u32 i = 0; i < pdata->rx_q_count; i++) {
+		val = rd32_mac(pdata, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+		fxgmac_set_bits(&val, MTL_Q_RQOMR_FUP_POS, MTL_Q_RQOMR_FUP_LEN,
+				1);
+		wr32_mac(pdata, val, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+	}
+}
+
+static void fxgmac_config_rx_buffer_size(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
+		fxgmac_set_bits(&val, DMA_CH_RCR_RBSZ_POS, DMA_CH_RCR_RBSZ_LEN,
+				pdata->rx_buf_size);
+		wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
+	}
+}
+
+static void fxgmac_config_tso_mode(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+	fxgmac_set_bits(&val, DMA_CH_TCR_TSE_POS, DMA_CH_TCR_TSE_LEN,
+			pdata->hw_feat.tso);
+	wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+}
+
+static void fxgmac_config_sph_mode(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_CR));
+		fxgmac_set_bits(&val, DMA_CH_CR_SPH_POS, DMA_CH_CR_SPH_LEN, 0);
+		wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_CR));
+	}
+
+	val = rd32_mac(pdata, MAC_ECR);
+	fxgmac_set_bits(&val, MAC_ECR_HDSMS_POS, MAC_ECR_HDSMS_LEN,
+			FXGMAC_SPH_HDSMS_SIZE_512B);
+	wr32_mac(pdata, val, MAC_ECR);
+}
+
+static unsigned int fxgmac_usec_to_riwt(struct fxgmac_pdata *pdata,
+					unsigned int usec)
+{
+	/* Convert the input usec value to the watchdog timer value. Each
+	 * watchdog timer value is equivalent to 256 clock cycles.
+	 * Calculate the required value as:
+	 *  ( usec * ( system_clock_mhz / 10^6) / 256
+	 */
+	return (usec * (pdata->sysclk_rate / 1000000)) / 256;
+}
+
+static void fxgmac_config_rx_threshold(struct fxgmac_pdata *pdata,
+				       unsigned int set_val)
+{
+	u32 val;
+
+	for (u32 i = 0; i < pdata->rx_q_count; i++) {
+		val = rd32_mac(pdata, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+		fxgmac_set_bits(&val, MTL_Q_RQOMR_RTC_POS, MTL_Q_RQOMR_RTC_LEN,
+				set_val);
+		wr32_mac(pdata, val, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+	}
+}
+
+static void fxgmac_config_mtl_mode(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	/* Set Tx to weighted round robin scheduling algorithm */
+	val = rd32_mac(pdata, MTL_OMR);
+	fxgmac_set_bits(&val, MTL_OMR_ETSALG_POS, MTL_OMR_ETSALG_LEN,
+			MTL_ETSALG_WRR);
+	wr32_mac(pdata, val, MTL_OMR);
+
+	/* Set Tx traffic classes to use WRR algorithm with equal weights */
+	val = rd32_mac(pdata, FXGMAC_MTL_REG(0, MTL_TC_QWR));
+	fxgmac_set_bits(&val, MTL_TC_QWR_QW_POS, MTL_TC_QWR_QW_LEN, 1);
+	wr32_mac(pdata, val, FXGMAC_MTL_REG(0, MTL_TC_QWR));
+
+	/* Set Rx to strict priority algorithm */
+	val = rd32_mac(pdata, MTL_OMR);
+	fxgmac_set_bits(&val, MTL_OMR_RAA_POS, MTL_OMR_RAA_LEN, MTL_RAA_SP);
+	wr32_mac(pdata, val, MTL_OMR);
+}
+
+static void fxgmac_config_queue_mapping(struct fxgmac_pdata *pdata)
+{
+	unsigned int ppq, ppq_extra, prio_queues;
+	unsigned int __maybe_unused prio;
+	unsigned int reg, val, mask;
+
+	/* Map the 8 VLAN priority values to available MTL Rx queues */
+	prio_queues =
+		min_t(unsigned int, IEEE_8021QAZ_MAX_TCS, pdata->rx_q_count);
+	ppq = IEEE_8021QAZ_MAX_TCS / prio_queues;
+	ppq_extra = IEEE_8021QAZ_MAX_TCS % prio_queues;
+
+	reg = MAC_RQC2R;
+	val = 0;
+	for (u32 i = 0, prio = 0; i < prio_queues;) {
+		mask = 0;
+		for (u32 j = 0; j < ppq; j++) {
+			yt_dbg(pdata, "PRIO%u mapped to RXq%u\n", prio, i);
+			mask |= (1 << prio);
+			prio++;
+		}
+
+		if (i < ppq_extra) {
+			yt_dbg(pdata, "PRIO%u mapped to RXq%u\n", prio, i);
+			mask |= (1 << prio);
+			prio++;
+		}
+
+		val |= (mask << ((i++ % MAC_RQC2_Q_PER_REG) << 3));
+
+		if ((i % MAC_RQC2_Q_PER_REG) && i != prio_queues)
+			continue;
+
+		wr32_mac(pdata, val, reg);
+		reg += MAC_RQC2_INC;
+		val = 0;
+	}
+
+	/* Configure one to one, MTL Rx queue to DMA Rx channel mapping
+	 * ie Q0 <--> CH0, Q1 <--> CH1 ... Q11 <--> CH11
+	 */
+	val = rd32_mac(pdata, MTL_RQDCM0R);
+	val |= (MTL_RQDCM0R_Q0MDMACH | MTL_RQDCM0R_Q1MDMACH |
+		MTL_RQDCM0R_Q2MDMACH | MTL_RQDCM0R_Q3MDMACH);
+
+	/* Selection to let RSS work, * ie, bit4,12,20,28 for
+	 * Q0,1,2,3 individual
+	 */
+	if (pdata->rss)
+		val |= (MTL_RQDCM0R_Q0DDMACH | MTL_RQDCM0R_Q1DDMACH |
+			MTL_RQDCM0R_Q2DDMACH | MTL_RQDCM0R_Q3DDMACH);
+
+	wr32_mac(pdata, val, MTL_RQDCM0R);
+
+	val = rd32_mac(pdata, MTL_RQDCM0R + MTL_RQDCM_INC);
+	val |= (MTL_RQDCM1R_Q4MDMACH | MTL_RQDCM1R_Q5MDMACH |
+		MTL_RQDCM1R_Q6MDMACH | MTL_RQDCM1R_Q7MDMACH);
+	wr32_mac(pdata, val, MTL_RQDCM0R + MTL_RQDCM_INC);
+}
+
+#define FXGMAC_MAX_FIFO 81920
+
+static unsigned int fxgmac_calculate_per_queue_fifo(unsigned int fifo_size,
+						    unsigned int queue_count)
+{
+	u32 q_fifo_size, p_fifo;
+
+	/* Calculate the configured fifo size */
+	q_fifo_size = 1 << (fifo_size + 7);
+
+	/* The configured value may not be the actual amount of fifo RAM */
+	q_fifo_size = min_t(unsigned int, FXGMAC_MAX_FIFO, q_fifo_size);
+	q_fifo_size = q_fifo_size / queue_count;
+
+	/* Each increment in the queue fifo size represents 256 bytes of
+	 * fifo, with 0 representing 256 bytes. Distribute the fifo equally
+	 * between the queues.
+	 */
+	p_fifo = q_fifo_size / 256;
+	if (p_fifo)
+		p_fifo--;
+
+	return p_fifo;
+}
+
+static u32 fxgmac_calculate_max_checksum_size(struct fxgmac_pdata *pdata)
+{
+	u32 fifo_size;
+
+	fifo_size = fxgmac_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size,
+						    FXGMAC_TX_1_Q);
+
+	/* Each increment in the queue fifo size represents 256 bytes of
+	 * fifo, with 0 representing 256 bytes. Distribute the fifo equally
+	 * between the queues.
+	 */
+	fifo_size = (fifo_size + 1) * 256;
+
+	/* Packet size < TxQSize - (PBL + N)*(DATAWIDTH/8),
+	 * Datawidth = 128
+	 * If Datawidth = 32, N = 7, elseif Datawidth != 32, N = 5.
+	 * TxQSize is indicated by TQS field of MTL_TxQ#_Operation_Mode register
+	 * PBL = TxPBL field in the DMA_CH#_TX_Control register in all
+	 * DMA configurations.
+	 */
+	fifo_size -= (pdata->tx_pbl * (pdata->pblx8 ? 8 : 1) + 5) *
+		     (FXGMAC_DATA_WIDTH / 8);
+	fifo_size -= 256;
+
+	return fifo_size;
+}
+
+static void fxgmac_config_tx_fifo_size(struct fxgmac_pdata *pdata)
+{
+	u32 val, fifo_size;
+
+	fifo_size = fxgmac_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size,
+						    FXGMAC_TX_1_Q);
+
+	val = rd32_mac(pdata, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+	fxgmac_set_bits(&val, MTL_Q_TQOMR_TQS_POS, MTL_Q_TQOMR_TQS_LEN,
+			fifo_size);
+	wr32_mac(pdata, val, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+
+	yt_dbg(pdata, "%d Tx hardware queues, %d byte fifo per queue\n",
+	       FXGMAC_TX_1_Q, ((fifo_size + 1) * 256));
+}
+
+static void fxgmac_config_rx_fifo_size(struct fxgmac_pdata *pdata)
+{
+	u32 val, fifo_size;
+
+	fifo_size = fxgmac_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size,
+						    pdata->rx_q_count);
+
+	for (u32 i = 0; i < pdata->rx_q_count; i++) {
+		val = rd32_mac(pdata, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+		fxgmac_set_bits(&val, MTL_Q_RQOMR_RQS_POS, MTL_Q_RQOMR_RQS_LEN,
+				fifo_size);
+		wr32_mac(pdata, val, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+	}
+
+	yt_dbg(pdata, "%d Rx hardware queues, %d byte fifo per queue\n",
+	       pdata->rx_q_count, ((fifo_size + 1) * 256));
+}
+
+static void fxgmac_config_flow_control_threshold(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	for (u32 i = 0; i < pdata->rx_q_count; i++) {
+		val = rd32_mac(pdata, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+		/* Activate flow control when less than 4k left in fifo */
+		fxgmac_set_bits(&val, MTL_Q_RQOMR_RFA_POS, MTL_Q_RQOMR_RFA_LEN,
+				6);
+		/* De-activate flow control when more than 6k left in fifo */
+		fxgmac_set_bits(&val, MTL_Q_RQOMR_RFD_POS, MTL_Q_RQOMR_RFD_LEN,
+				10);
+		wr32_mac(pdata, val, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+	}
+}
+
+static void fxgmac_config_tx_threshold(struct fxgmac_pdata *pdata,
+				       unsigned int set_val)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+	fxgmac_set_bits(&val, MTL_Q_TQOMR_TTC_POS, MTL_Q_TQOMR_TTC_LEN,
+			set_val);
+	wr32_mac(pdata, val, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+}
+
+static void fxgmac_config_rsf_mode(struct fxgmac_pdata *pdata,
+				   unsigned int set_val)
+{
+	u32 val;
+
+	for (u32 i = 0; i < pdata->rx_q_count; i++) {
+		val = rd32_mac(pdata, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+		fxgmac_set_bits(&val, MTL_Q_RQOMR_RSF_POS, MTL_Q_RQOMR_RSF_LEN,
+				set_val);
+		wr32_mac(pdata, val, FXGMAC_MTL_REG(i, MTL_Q_RQOMR));
+	}
+}
+
+static void fxgmac_config_tsf_mode(struct fxgmac_pdata *pdata,
+				   unsigned int set_val)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+	fxgmac_set_bits(&val, MTL_Q_TQOMR_TSF_POS, MTL_Q_TQOMR_TSF_LEN,
+			set_val);
+	wr32_mac(pdata, val, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+}
+
+static void fxgmac_config_osp_mode(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+	fxgmac_set_bits(&val, DMA_CH_TCR_OSP_POS, DMA_CH_TCR_OSP_LEN,
+			pdata->tx_osp_mode);
+	wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+}
+
+static void fxgmac_config_pblx8(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_CR));
+		fxgmac_set_bits(&val, DMA_CH_CR_PBLX8_POS, DMA_CH_CR_PBLX8_LEN,
+				pdata->pblx8);
+		wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_CR));
+	}
+}
+
+static void fxgmac_config_tx_pbl_val(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+	fxgmac_set_bits(&val, DMA_CH_TCR_PBL_POS, DMA_CH_TCR_PBL_LEN,
+			pdata->tx_pbl);
+	wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_TCR));
+}
+
+static void fxgmac_config_rx_pbl_val(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val;
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
+		fxgmac_set_bits(&val, DMA_CH_RCR_PBL_POS, DMA_CH_RCR_PBL_LEN,
+				pdata->rx_pbl);
+		wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
+	}
+}
+
+static void fxgmac_tx_mmc_int(struct fxgmac_pdata *yt)
+{
+	unsigned int mmc_isr = rd32_mac(yt, MMC_TISR);
+	struct fxgmac_stats *stats = &yt->stats;
+
+	if (mmc_isr & BIT(MMC_TISR_TXOCTETCOUNT_GB_POS))
+		stats->txoctetcount_gb += rd32_mac(yt, MMC_TXOCTETCOUNT_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXFRAMECOUNT_GB_POS))
+		stats->txframecount_gb += rd32_mac(yt, MMC_TXFRAMECOUNT_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXBROADCASTFRAMES_G_POS))
+		stats->txbroadcastframes_g +=
+			rd32_mac(yt, MMC_TXBROADCASTFRAMES_G_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXMULTICASTFRAMES_G_POS))
+		stats->txmulticastframes_g +=
+			rd32_mac(yt, MMC_TXMULTICASTFRAMES_G_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TX64OCTETS_GB_POS))
+		stats->tx64octets_gb += rd32_mac(yt, MMC_TX64OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TX65TO127OCTETS_GB_POS))
+		stats->tx65to127octets_gb +=
+			rd32_mac(yt, MMC_TX65TO127OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TX128TO255OCTETS_GB_POS))
+		stats->tx128to255octets_gb +=
+			rd32_mac(yt, MMC_TX128TO255OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TX256TO511OCTETS_GB_POS))
+		stats->tx256to511octets_gb +=
+			rd32_mac(yt, MMC_TX256TO511OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TX512TO1023OCTETS_GB_POS))
+		stats->tx512to1023octets_gb +=
+			rd32_mac(yt, MMC_TX512TO1023OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TX1024TOMAXOCTETS_GB_POS))
+		stats->tx1024tomaxoctets_gb +=
+			rd32_mac(yt, MMC_TX1024TOMAXOCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXUNICASTFRAMES_GB_POS))
+		stats->txunicastframes_gb +=
+			rd32_mac(yt, MMC_TXUNICASTFRAMES_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXMULTICASTFRAMES_GB_POS))
+		stats->txmulticastframes_gb +=
+			rd32_mac(yt, MMC_TXMULTICASTFRAMES_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXBROADCASTFRAMES_GB_POS))
+		stats->txbroadcastframes_g +=
+			rd32_mac(yt, MMC_TXBROADCASTFRAMES_GB_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXUNDERFLOWERROR_POS))
+		stats->txunderflowerror +=
+			rd32_mac(yt, MMC_TXUNDERFLOWERROR_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXSINGLECOLLISION_G_POS))
+		stats->txsinglecollision_g +=
+			rd32_mac(yt, MMC_TXSINGLECOLLISION_G);
+
+	if (mmc_isr & BIT(MMC_TISR_TXMULTIPLECOLLISION_G_POS))
+		stats->txmultiplecollision_g +=
+			rd32_mac(yt, MMC_TXMULTIPLECOLLISION_G);
+
+	if (mmc_isr & BIT(MMC_TISR_TXDEFERREDFRAMES_POS))
+		stats->txdeferredframes += rd32_mac(yt, MMC_TXDEFERREDFRAMES);
+
+	if (mmc_isr & BIT(MMC_TISR_TXLATECOLLISIONFRAMES_POS))
+		stats->txlatecollisionframes +=
+			rd32_mac(yt, MMC_TXLATECOLLISIONFRAMES);
+
+	if (mmc_isr & BIT(MMC_TISR_TXEXCESSIVECOLLISIONFRAMES_POS))
+		stats->txexcessivecollisionframes +=
+			rd32_mac(yt, MMC_TXEXCESSIVECOLLSIONFRAMES);
+
+	if (mmc_isr & BIT(MMC_TISR_TXCARRIERERRORFRAMES_POS))
+		stats->txcarriererrorframes +=
+			rd32_mac(yt, MMC_TXCARRIERERRORFRAMES);
+
+	if (mmc_isr & BIT(MMC_TISR_TXOCTETCOUNT_G_POS))
+		stats->txoctetcount_g += rd32_mac(yt, MMC_TXOCTETCOUNT_G_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXFRAMECOUNT_G_POS))
+		stats->txframecount_g += rd32_mac(yt, MMC_TXFRAMECOUNT_G_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXEXCESSIVEDEFERRALFRAMES_POS))
+		stats->txexcessivedeferralerror +=
+			rd32_mac(yt, MMC_TXEXCESSIVEDEFERRALERROR);
+
+	if (mmc_isr & BIT(MMC_TISR_TXPAUSEFRAMES_POS))
+		stats->txpauseframes += rd32_mac(yt, MMC_TXPAUSEFRAMES_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXVLANFRAMES_G_POS))
+		stats->txvlanframes_g += rd32_mac(yt, MMC_TXVLANFRAMES_G_LO);
+
+	if (mmc_isr & BIT(MMC_TISR_TXOVERSIZE_G_POS))
+		stats->txoversize_g += rd32_mac(yt, MMC_TXOVERSIZEFRAMES);
+}
+
+static void fxgmac_rx_mmc_int(struct fxgmac_pdata *yt)
+{
+	unsigned int mmc_isr = rd32_mac(yt, MMC_RISR);
+	struct fxgmac_stats *stats = &yt->stats;
+
+	if (mmc_isr & BIT(MMC_RISR_RXFRAMECOUNT_GB_POS))
+		stats->rxframecount_gb += rd32_mac(yt, MMC_RXFRAMECOUNT_GB_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXOCTETCOUNT_GB_POS))
+		stats->rxoctetcount_gb += rd32_mac(yt, MMC_RXOCTETCOUNT_GB_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXOCTETCOUNT_G_POS))
+		stats->rxoctetcount_g += rd32_mac(yt, MMC_RXOCTETCOUNT_G_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXBROADCASTFRAMES_G_POS))
+		stats->rxbroadcastframes_g +=
+			rd32_mac(yt, MMC_RXBROADCASTFRAMES_G_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXMULTICASTFRAMES_G_POS))
+		stats->rxmulticastframes_g +=
+			rd32_mac(yt, MMC_RXMULTICASTFRAMES_G_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXCRCERROR_POS))
+		stats->rxcrcerror += rd32_mac(yt, MMC_RXCRCERROR_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXALIGNERROR_POS))
+		stats->rxalignerror += rd32_mac(yt, MMC_RXALIGNERROR);
+
+	if (mmc_isr & BIT(MMC_RISR_RXRUNTERROR_POS))
+		stats->rxrunterror += rd32_mac(yt, MMC_RXRUNTERROR);
+
+	if (mmc_isr & BIT(MMC_RISR_RXJABBERERROR_POS))
+		stats->rxjabbererror += rd32_mac(yt, MMC_RXJABBERERROR);
+
+	if (mmc_isr & BIT(MMC_RISR_RXUNDERSIZE_G_POS))
+		stats->rxundersize_g += rd32_mac(yt, MMC_RXUNDERSIZE_G);
+
+	if (mmc_isr & BIT(MMC_RISR_RXOVERSIZE_G_POS))
+		stats->rxoversize_g += rd32_mac(yt, MMC_RXOVERSIZE_G);
+
+	if (mmc_isr & BIT(MMC_RISR_RX64OCTETS_GB_POS))
+		stats->rx64octets_gb += rd32_mac(yt, MMC_RX64OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RX65TO127OCTETS_GB_POS))
+		stats->rx65to127octets_gb +=
+			rd32_mac(yt, MMC_RX65TO127OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RX128TO255OCTETS_GB_POS))
+		stats->rx128to255octets_gb +=
+			rd32_mac(yt, MMC_RX128TO255OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RX256TO511OCTETS_GB_POS))
+		stats->rx256to511octets_gb +=
+			rd32_mac(yt, MMC_RX256TO511OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RX512TO1023OCTETS_GB_POS))
+		stats->rx512to1023octets_gb +=
+			rd32_mac(yt, MMC_RX512TO1023OCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RX1024TOMAXOCTETS_GB_POS))
+		stats->rx1024tomaxoctets_gb +=
+			rd32_mac(yt, MMC_RX1024TOMAXOCTETS_GB_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXUNICASTFRAMES_G_POS))
+		stats->rxunicastframes_g +=
+			rd32_mac(yt, MMC_RXUNICASTFRAMES_G_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXLENGTHERROR_POS))
+		stats->rxlengtherror += rd32_mac(yt, MMC_RXLENGTHERROR_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXOUTOFRANGETYPE_POS))
+		stats->rxoutofrangetype +=
+			rd32_mac(yt, MMC_RXOUTOFRANGETYPE_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXPAUSEFRAMES_POS))
+		stats->rxpauseframes += rd32_mac(yt, MMC_RXPAUSEFRAMES_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXFIFOOVERFLOW_POS))
+		stats->rxfifooverflow += rd32_mac(yt, MMC_RXFIFOOVERFLOW_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXVLANFRAMES_GB_POS))
+		stats->rxvlanframes_gb += rd32_mac(yt, MMC_RXVLANFRAMES_GB_LO);
+
+	if (mmc_isr & BIT(MMC_RISR_RXWATCHDOGERROR_POS))
+		stats->rxwatchdogerror += rd32_mac(yt, MMC_RXWATCHDOGERROR);
+
+	if (mmc_isr & BIT(MMC_RISR_RXERRORFRAMES_POS))
+		stats->rxreceiveerrorframe +=
+			rd32_mac(yt, MMC_RXRECEIVEERRORFRAME);
+
+	if (mmc_isr & BIT(MMC_RISR_RXERRORCONTROLFRAMES_POS))
+		stats->rxcontrolframe_g += rd32_mac(yt, MMC_RXCONTROLFRAME_G);
+}
+
+static void fxgmac_config_mmc(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	/* Set counters to reset on read, Reset the counters */
+	val = rd32_mac(pdata, MMC_CR);
+	fxgmac_set_bits(&val, MMC_CR_ROR_POS, MMC_CR_ROR_LEN, 1);
+	fxgmac_set_bits(&val, MMC_CR_CR_POS, MMC_CR_CR_LEN, 1);
+	wr32_mac(pdata, val, MMC_CR);
+
+	wr32_mac(pdata, 0xffffffff, MMC_IPCRXINTMASK);
+}
+
+static u32 fxgmac_read_rss_options(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mem(pdata, MGMT_RSS_CTRL);
+	val = FXGMAC_GET_BITS(val, MGMT_RSS_CTRL_OPT_POS,
+			      MGMT_RSS_CTRL_OPT_LEN);
+
+	return val;
+}
+
+static void fxgmac_write_rss_options(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mem(pdata, MGMT_RSS_CTRL);
+	fxgmac_set_bits(&val, MGMT_RSS_CTRL_OPT_POS, MGMT_RSS_CTRL_OPT_LEN,
+			pdata->rss_options);
+	wr32_mem(pdata, val, MGMT_RSS_CTRL);
+}
+
+static void fxgmac_write_rss_hash_key(struct fxgmac_pdata *pdata)
+{
+	unsigned int key_regs = sizeof(pdata->rss_key) / sizeof(u32);
+	u32 *key = (u32 *)&pdata->rss_key;
+
+	while (key_regs--) {
+		wr32_mem(pdata, cpu_to_be32(*key),
+			 MGMT_RSS_KEY0 + key_regs * MGMT_RSS_KEY_INC);
+		key++;
+	}
+}
+
+static void fxgmac_write_rss_lookup_table(struct fxgmac_pdata *pdata)
+{
+	u32 i, j, val = 0;
+
+	for (i = 0, j = 0; i < ARRAY_SIZE(pdata->rss_table); i++, j++) {
+		if (j < MGMT_RSS_IDT_ENTRY_SIZE) {
+			val |= ((pdata->rss_table[i] & MGMT_RSS_IDT_ENTRY_MASK)
+				<< (j * 2));
+			continue;
+		}
+
+		wr32_mem(pdata, val,
+			 MGMT_RSS_IDT + (i / MGMT_RSS_IDT_ENTRY_SIZE - 1) *
+						MGMT_RSS_IDT_INC);
+		val = pdata->rss_table[i];
+		j = 0;
+	}
+
+	if (j != MGMT_RSS_IDT_ENTRY_SIZE)
+		return;
+
+	/* Last IDT */
+	wr32_mem(pdata, val,
+		 MGMT_RSS_IDT +
+			 (i / MGMT_RSS_IDT_ENTRY_SIZE - 1) * MGMT_RSS_IDT_INC);
+}
+
+static void fxgmac_set_rss_hash_key(struct fxgmac_pdata *pdata, const u8 *key)
+{
+	memcpy(pdata->rss_key, key, sizeof(pdata->rss_key));
+	fxgmac_write_rss_hash_key(pdata);
+}
+
+static u32 log2ex(u32 value)
+{
+	u32 i = 31;
+
+	while (i > 0) {
+		if (value & 0x80000000)
+			break;
+
+		value <<= 1;
+		i--;
+	}
+	return i;
+}
+
+static void fxgmac_enable_rss(struct fxgmac_pdata *pdata)
+{
+	u32 val, size = 0;
+
+	fxgmac_write_rss_hash_key(pdata);
+	fxgmac_write_rss_lookup_table(pdata);
+
+	/* Set RSS IDT table size, Enable RSS, Set the RSS options */
+	val = rd32_mem(pdata, MGMT_RSS_CTRL);
+	size = log2ex(FXGMAC_RSS_MAX_TABLE_SIZE) - 1;
+	fxgmac_set_bits(&val, MGMT_RSS_CTRL_TBL_SIZE_POS,
+			MGMT_RSS_CTRL_TBL_SIZE_LEN, size);
+	fxgmac_set_bits(&val, MGMT_RSS_CTRL_RSSE_POS, MGMT_RSS_CTRL_RSSE_LEN,
+			1);
+	fxgmac_set_bits(&val, MGMT_RSS_CTRL_OPT_POS, MGMT_RSS_CTRL_OPT_LEN,
+			pdata->rss_options);
+	wr32_mem(pdata, val, MGMT_RSS_CTRL);
+}
+
+static void fxgmac_disable_rss(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mem(pdata, MGMT_RSS_CTRL);
+	fxgmac_set_bits(&val, MGMT_RSS_CTRL_RSSE_POS, MGMT_RSS_CTRL_RSSE_LEN,
+			0);
+	wr32_mem(pdata, val, MGMT_RSS_CTRL);
+}
+
+static void fxgmac_config_rss(struct fxgmac_pdata *pdata)
+{
+	if (pdata->rss)
+		fxgmac_enable_rss(pdata);
+	else
+		fxgmac_disable_rss(pdata);
+}
+
+static int fxgmac_check_wake_pattern_fifo_pointer(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_PMT_STA);
+	fxgmac_set_bits(&val, MAC_PMT_STA_RWKFILTERST_POS,
+			MAC_PMT_STA_RWKFILTERST_LEN, 1);
+	wr32_mac(pdata, val, MAC_PMT_STA);
+
+	val = rd32_mac(pdata, MAC_PMT_STA);
+	val = FXGMAC_GET_BITS(val, MAC_PMT_STA_RWKPTR_POS,
+			      MAC_PMT_STA_RWKPTR_LEN);
+	if (val != 0) {
+		yt_err(pdata, "Remote fifo pointer is not 0\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void fxgmac_enable_dma_interrupts(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 dma_ch;
+
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		/* Clear all the interrupts which are set */
+		dma_ch = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_SR));
+		wr32_mac(pdata, dma_ch, FXGMAC_DMA_REG(channel, DMA_CH_SR));
+
+		dma_ch = 0; /* Clear all interrupt enable bits */
+
+		/* Enable following interrupts
+		 * NIE  - Normal Interrupt Summary Enable
+		 * FBEE - Fatal Bus Error Enable
+		 */
+		fxgmac_set_bits(&dma_ch, DMA_CH_IER_NIE_POS, DMA_CH_IER_NIE_LEN,
+				1);
+		fxgmac_set_bits(&dma_ch, DMA_CH_IER_FBEE_POS,
+				DMA_CH_IER_FBEE_LEN, 1);
+
+		if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i) && channel->tx_ring) {
+			/* Enable the following Tx interrupts
+			 * TIE  - Transmit Interrupt Enable (unless using
+			 * per channel interrupts)
+			 */
+			fxgmac_set_bits(&dma_ch, DMA_CH_IER_TIE_POS,
+					DMA_CH_IER_TIE_LEN, 1);
+		}
+		if (channel->rx_ring) {
+			/* Enable following Rx interrupts
+			 * RBUE - Receive Buffer Unavailable Enable
+			 * RIE  - Receive Interrupt Enable (unless using
+			 * per channel interrupts)
+			 */
+			fxgmac_set_bits(&dma_ch, DMA_CH_IER_RBUE_POS,
+					DMA_CH_IER_RBUE_LEN, 1);
+			fxgmac_set_bits(&dma_ch, DMA_CH_IER_RIE_POS,
+					DMA_CH_IER_RIE_LEN, 1);
+		}
+
+		wr32_mac(pdata, dma_ch, FXGMAC_DMA_REG(channel, DMA_CH_IER));
+	}
+}
+
+static void fxgmac_enable_mtl_interrupts(struct fxgmac_pdata *pdata)
+{
+	unsigned int mtl_q_isr;
+
+	for (u32 i = 0; i < pdata->hw_feat.rx_q_cnt; i++) {
+		/* Clear all the interrupts which are set */
+		mtl_q_isr = rd32_mac(pdata, FXGMAC_MTL_REG(i, MTL_Q_ISR));
+		wr32_mac(pdata, mtl_q_isr, FXGMAC_MTL_REG(i, MTL_Q_ISR));
+
+		/* No MTL interrupts to be enabled */
+		wr32_mac(pdata, 0, FXGMAC_MTL_REG(i, MTL_Q_IER));
+	}
+}
+
+#define FXGMAC_MMC_IER_ALL_DEFAULT 0
+static void fxgmac_enable_mac_interrupts(struct fxgmac_pdata *pdata)
+{
+	u32 val, ier = 0;
+
+	/* Enable Timestamp interrupt */
+	fxgmac_set_bits(&ier, MAC_IER_TSIE_POS, MAC_IER_TSIE_LEN, 1);
+	wr32_mac(pdata, ier, MAC_IER);
+
+	val = rd32_mac(pdata, MMC_RIER);
+	fxgmac_set_bits(&val, MMC_RIER_ALL_INTERRUPTS_POS,
+			MMC_RIER_ALL_INTERRUPTS_LEN,
+			FXGMAC_MMC_IER_ALL_DEFAULT);
+	wr32_mac(pdata, val, MMC_RIER);
+
+	val = rd32_mac(pdata, MMC_TIER);
+	fxgmac_set_bits(&val, MMC_TIER_ALL_INTERRUPTS_POS,
+			MMC_TIER_ALL_INTERRUPTS_LEN,
+			FXGMAC_MMC_IER_ALL_DEFAULT);
+	wr32_mac(pdata, val, MMC_TIER);
+}
+
+static void fxgmac_config_mac_speed(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, MAC_CR);
+	if (pdata->phy_duplex != DUPLEX_UNKNOWN)
+		fxgmac_set_bits(&val, MAC_CR_DM_POS, MAC_CR_DM_LEN, pdata->phy_duplex);
+
+	switch (pdata->phy_speed) {
+	case SPEED_1000:
+		fxgmac_set_bits(&val, MAC_CR_PS_POS, MAC_CR_PS_LEN, 0);
+		fxgmac_set_bits(&val, MAC_CR_FES_POS, MAC_CR_FES_LEN, 0);
+		break;
+	case SPEED_100:
+		fxgmac_set_bits(&val, MAC_CR_PS_POS, MAC_CR_PS_LEN, 1);
+		fxgmac_set_bits(&val, MAC_CR_FES_POS, MAC_CR_FES_LEN, 1);
+		break;
+	case SPEED_10:
+		fxgmac_set_bits(&val, MAC_CR_PS_POS, MAC_CR_PS_LEN, 1);
+		fxgmac_set_bits(&val, MAC_CR_FES_POS, MAC_CR_FES_LEN, 0);
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+
+	wr32_mac(pdata, val, MAC_CR);
+}
+
+static int mdio_loop_wait(struct fxgmac_pdata *pdata, u32 max_cnt)
+{
+	u32 val, i;
+
+	for (i = 0; i < max_cnt; i++) {
+		val = rd32_mac(pdata, MAC_MDIO_ADDRESS);
+		if ((val & MAC_MDIO_ADDR_BUSY) == 0)
+			break;
+
+		fsleep(10);
+	}
+
+	if (i >= max_cnt) {
+		WARN_ON(1);
+		yt_err(pdata, "%s timeout. used cnt:%d, reg_val=%x.\n",
+		       __func__, i + 1, val);
+
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+#define PHY_MDIO_MAX_TRY			50
+#define PHY_WR_CONFIG(reg_offset)		(0x8000205 + ((reg_offset) * 0x10000))
+static int fxgmac_phy_write_reg(struct fxgmac_pdata *pdata, u32 reg_id, u32 data)
+{
+	int ret;
+
+	wr32_mac(pdata, data, MAC_MDIO_DATA);
+	wr32_mac(pdata, PHY_WR_CONFIG(reg_id), MAC_MDIO_ADDRESS);
+	ret = mdio_loop_wait(pdata, PHY_MDIO_MAX_TRY);
+	if (ret < 0)
+		return ret;
+
+	yt_dbg(pdata, "%s, id:%x %s, ctrl:0x%08x, data:0x%08x\n", __func__,
+	       reg_id, (ret == 0) ? "ok" : "err", PHY_WR_CONFIG(reg_id), data);
+
+	return ret;
+}
+
+#define PHY_RD_CONFIG(reg_offset)		(0x800020d + ((reg_offset) * 0x10000))
+static int fxgmac_phy_read_reg(struct fxgmac_pdata *pdata, u32 reg_id)
+{
+	u32 val;
+	int ret;
+
+	wr32_mac(pdata, PHY_RD_CONFIG(reg_id), MAC_MDIO_ADDRESS);
+	ret =  mdio_loop_wait(pdata, PHY_MDIO_MAX_TRY);
+	if (ret < 0)
+		return ret;
+
+	val = rd32_mac(pdata, MAC_MDIO_DATA);  /* Read data */
+	yt_dbg(pdata, "%s, id:%x ok, ctrl:0x%08x, val:0x%08x.\n", __func__,
+	       reg_id, PHY_RD_CONFIG(reg_id), val);
+
+	return val;
+}
+
+static int fxgmac_config_flow_control(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0;
+	int ret;
+
+	fxgmac_config_tx_flow_control(pdata);
+	fxgmac_config_rx_flow_control(pdata);
+
+	/* Set auto negotiation advertisement pause ability */
+	if (pdata->tx_pause || pdata->rx_pause)
+		val |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+
+	ret = phy_modify(pdata->phydev, MII_ADVERTISE,
+			 ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, val);
+	if (ret < 0)
+		return ret;
+
+	return phy_modify(pdata->phydev, MII_BMCR, BMCR_RESET, BMCR_RESET);
+}
+
+static void fxgmac_phy_reset(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0;
+
+	fxgmac_set_bits(&val, EPHY_CTRL_RESET_POS, EPHY_CTRL_RESET_LEN,
+			EPHY_CTRL_STA_RESET);
+	wr32_mem(pdata, val, EPHY_CTRL);
+	fsleep(1500);
+}
+
+static void fxgmac_phy_release(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0;
+
+	fxgmac_set_bits(&val, EPHY_CTRL_RESET_POS, EPHY_CTRL_RESET_LEN,
+			EPHY_CTRL_STA_RELEASE);
+	wr32_mem(pdata, val, EPHY_CTRL);
+	fsleep(100);
+}
+
+static void fxgmac_enable_channel_irq(struct fxgmac_channel *channel,
+				      enum fxgmac_int int_id)
+{
+	struct fxgmac_pdata *pdata = channel->pdata;
+	unsigned int dma_ch_ier;
+
+	dma_ch_ier = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_IER));
+
+	switch (int_id) {
+	case FXGMAC_INT_DMA_CH_SR_TI:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_TIE_POS,
+				DMA_CH_IER_TIE_LEN, 1);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_TPS:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_TXSE_POS,
+				DMA_CH_IER_TXSE_LEN, 1);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_TBU:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_TBUE_POS,
+				DMA_CH_IER_TBUE_LEN, 1);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_RI:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_RIE_POS,
+				DMA_CH_IER_RIE_LEN, 1);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_RBU:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_RBUE_POS,
+				DMA_CH_IER_RBUE_LEN, 1);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_RPS:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_RSE_POS,
+				DMA_CH_IER_RSE_LEN, 1);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_TI_RI:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_TIE_POS,
+				DMA_CH_IER_TIE_LEN, 1);
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_RIE_POS,
+				DMA_CH_IER_RIE_LEN, 1);
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_NIE_POS,
+				DMA_CH_IER_NIE_LEN, 1);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_FBE:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_FBEE_POS,
+				DMA_CH_IER_FBEE_LEN, 1);
+		break;
+	case FXGMAC_INT_DMA_ALL:
+		dma_ch_ier |= channel->saved_ier;
+		break;
+	default:
+		WARN_ON(1);
+		return;
+	}
+
+	wr32_mac(pdata, dma_ch_ier, FXGMAC_DMA_REG(channel, DMA_CH_IER));
+}
+
+static void fxgmac_disable_channel_irq(struct fxgmac_channel *channel,
+				       enum fxgmac_int int_id)
+{
+	struct fxgmac_pdata *pdata = channel->pdata;
+	u32 dma_ch_ier;
+
+	dma_ch_ier = rd32_mac(channel->pdata,
+			      FXGMAC_DMA_REG(channel, DMA_CH_IER));
+
+	switch (int_id) {
+	case FXGMAC_INT_DMA_CH_SR_TI:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_TIE_POS,
+				DMA_CH_IER_TIE_LEN, 0);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_TPS:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_TXSE_POS,
+				DMA_CH_IER_TXSE_LEN, 0);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_TBU:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_TBUE_POS,
+				DMA_CH_IER_TBUE_LEN, 0);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_RI:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_RIE_POS,
+				DMA_CH_IER_RIE_LEN, 0);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_RBU:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_RBUE_POS,
+				DMA_CH_IER_RBUE_LEN, 0);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_RPS:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_RSE_POS,
+				DMA_CH_IER_RSE_LEN, 0);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_TI_RI:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_TIE_POS,
+				DMA_CH_IER_TIE_LEN, 0);
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_RIE_POS,
+				DMA_CH_IER_RIE_LEN, 0);
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_NIE_POS,
+				DMA_CH_IER_NIE_LEN, 0);
+		break;
+	case FXGMAC_INT_DMA_CH_SR_FBE:
+		fxgmac_set_bits(&dma_ch_ier, DMA_CH_IER_FBEE_POS,
+				DMA_CH_IER_FBEE_LEN, 0);
+		break;
+	case FXGMAC_INT_DMA_ALL:
+#define FXGMAC_DMA_INTERRUPT_MASK 0x31c7
+		channel->saved_ier = dma_ch_ier & FXGMAC_DMA_INTERRUPT_MASK;
+		dma_ch_ier &= ~FXGMAC_DMA_INTERRUPT_MASK;
+		break;
+	default:
+		WARN_ON(1);
+		return;
+	}
+
+	wr32_mac(pdata, dma_ch_ier, FXGMAC_DMA_REG(channel, DMA_CH_IER));
+}
+
+static void fxgmac_dismiss_all_int(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_channel *channel = pdata->channel_head;
+	u32 val, i;
+
+	for (i = 0; i < pdata->channel_count; i++, channel++) {
+		/* Clear all the interrupts which are set */
+		val = rd32_mac(pdata, FXGMAC_DMA_REG(channel, DMA_CH_SR));
+		wr32_mac(pdata, val, FXGMAC_DMA_REG(channel, DMA_CH_SR));
+	}
+
+	for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++) {
+		/* Clear all the interrupts which are set */
+		val = rd32_mac(pdata, FXGMAC_MTL_REG(i, MTL_Q_ISR));
+		wr32_mac(pdata, val, FXGMAC_MTL_REG(i, MTL_Q_ISR));
+	}
+
+	rd32_mac(pdata, MAC_ISR); /* Clear all MAC interrupts */
+	/* Clear MAC tx/rx error interrupts */
+	rd32_mac(pdata, MAC_TX_RX_STA);
+	rd32_mac(pdata, MAC_PMT_STA);
+	rd32_mac(pdata, MAC_LPI_STA);
+
+	val = rd32_mac(pdata, MAC_DBG_STA);
+	wr32_mac(pdata, val, MAC_DBG_STA); /* Write clear */
+}
+
+static void fxgmac_set_interrupt_moderation(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0, time;
+
+	time = (pdata->intr_mod) ? pdata->tx_usecs : 0;
+	fxgmac_set_bits(&val, INT_MOD_TX_POS, INT_MOD_TX_LEN, time);
+
+	time = (pdata->intr_mod) ? pdata->rx_usecs : 0;
+	fxgmac_set_bits(&val, INT_MOD_RX_POS, INT_MOD_RX_LEN, time);
+	wr32_mem(pdata, val, INT_MOD);
+}
+
+static void fxgmac_enable_msix_one_irq(struct fxgmac_pdata *pdata, u32 intid)
+{
+	wr32_mem(pdata, 0, MSIX_TBL_MASK + intid * 16);
+}
+
+static void fxgmac_disable_msix_one_irq(struct fxgmac_pdata *pdata, u32 intid)
+{
+	wr32_mem(pdata, 1, MSIX_TBL_MASK + intid * 16);
+}
+
+static void fxgmac_enable_mgm_irq(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0;
+
+	fxgmac_set_bits(&val, MGMT_INT_CTRL0_INT_MASK_POS,
+			MGMT_INT_CTRL0_INT_MASK_LEN,
+			MGMT_INT_CTRL0_INT_MASK_DISABLE);
+	wr32_mem(pdata, val, MGMT_INT_CTRL0);
+}
+
+static void fxgmac_disable_mgm_irq(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0;
+
+	fxgmac_set_bits(&val, MGMT_INT_CTRL0_INT_MASK_POS,
+			MGMT_INT_CTRL0_INT_MASK_LEN,
+			MGMT_INT_CTRL0_INT_MASK_MASK);
+	wr32_mem(pdata, val, MGMT_INT_CTRL0);
+}
+
+static int fxgmac_flush_tx_queues(struct fxgmac_pdata *pdata)
+{
+	u32 val, count;
+
+	val = rd32_mac(pdata, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+	fxgmac_set_bits(&val, MTL_Q_TQOMR_FTQ_POS, MTL_Q_TQOMR_FTQ_LEN,
+			1);
+	wr32_mac(pdata, val, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+
+	count = 2000;
+	do {
+		fsleep(20);
+		val = rd32_mac(pdata, FXGMAC_MTL_REG(0, MTL_Q_TQOMR));
+		val = FXGMAC_GET_BITS(val, MTL_Q_TQOMR_FTQ_POS,
+				      MTL_Q_TQOMR_FTQ_LEN);
+
+	} while (--count && val);
+
+	if (val)
+		return -EBUSY;
+
+	return 0;
+}
+
+static void fxgmac_config_dma_bus(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mac(pdata, DMA_SBMR);
+	/* Set enhanced addressing mode */
+	fxgmac_set_bits(&val, DMA_SBMR_EAME_POS, DMA_SBMR_EAME_LEN, 1);
+
+	/* Out standing read/write requests */
+	fxgmac_set_bits(&val, DMA_SBMR_RD_OSR_LMT_POS, DMA_SBMR_RD_OSR_LMT_LEN,
+			0x7);
+	fxgmac_set_bits(&val, DMA_SBMR_WR_OSR_LMT_POS, DMA_SBMR_WR_OSR_LMT_LEN,
+			0x7);
+
+	/* Set the System Bus mode */
+	fxgmac_set_bits(&val, DMA_SBMR_FB_POS, DMA_SBMR_FB_LEN, 0);
+	fxgmac_set_bits(&val, DMA_SBMR_BLEN_4_POS, DMA_SBMR_BLEN_4_LEN, 1);
+	fxgmac_set_bits(&val, DMA_SBMR_BLEN_8_POS, DMA_SBMR_BLEN_8_LEN, 1);
+	fxgmac_set_bits(&val, DMA_SBMR_BLEN_16_POS, DMA_SBMR_BLEN_16_LEN, 1);
+	fxgmac_set_bits(&val, DMA_SBMR_BLEN_32_POS, DMA_SBMR_BLEN_32_LEN, 1);
+	wr32_mac(pdata, val, DMA_SBMR);
+}
+
+static int fxgmac_phy_clear_interrupt(struct fxgmac_pdata *pdata)
+{
+	u32 stats_pre, stats;
+
+	if (mutex_trylock(&pdata->phydev->mdio.bus->mdio_lock) == 0) {
+		yt_dbg(pdata, "lock not ready!\n");
+		return 0;
+	}
+
+	stats_pre = fxgmac_phy_read_reg(pdata, PHY_INT_STATUS);
+	if (stats_pre < 0)
+		goto unlock;
+
+	stats = fxgmac_phy_read_reg(pdata, PHY_INT_STATUS);
+	if (stats < 0)
+		goto unlock;
+
+	phy_unlock_mdio_bus(pdata->phydev);
+
+#define LINK_DOWN	0x800
+#define LINK_UP		0x400
+#define LINK_CHANGE	(LINK_DOWN | LINK_UP)
+	if ((stats_pre & LINK_CHANGE) != (stats & LINK_CHANGE)) {
+		yt_dbg(pdata, "phy link change\n");
+		return 1;
+	}
+
+	return 0;
+unlock:
+	phy_unlock_mdio_bus(pdata->phydev);
+	yt_err(pdata, "fxgmac_phy_read_reg err!\n");
+	return  -ETIMEDOUT;
+}
+
+#define FXGMAC_WOL_WAIT_2_MS 2
+
+static void fxgmac_config_wol_wait_time(struct fxgmac_pdata *pdata)
+{
+	u32 val;
+
+	val = rd32_mem(pdata, WOL_CTRL);
+	fxgmac_set_bits(&val, WOL_WAIT_TIME_POS, WOL_WAIT_TIME_LEN,
+			FXGMAC_WOL_WAIT_2_MS);
+	wr32_mem(pdata, val, WOL_CTRL);
+}
+
+static void fxgmac_desc_rx_channel_init(struct fxgmac_channel *channel)
+{
+	struct fxgmac_pdata *pdata = channel->pdata;
+	struct fxgmac_ring *ring = channel->rx_ring;
+	unsigned int start_index = ring->cur;
+	struct fxgmac_desc_data *desc_data;
+
+	/* Initialize all descriptors */
+	for (u32 i = 0; i < ring->dma_desc_count; i++) {
+		desc_data = FXGMAC_GET_DESC_DATA(ring, i);
+
+		/* Initialize Rx descriptor */
+		fxgmac_desc_rx_reset(desc_data);
+	}
+
+	/* Update the total number of Rx descriptors */
+	wr32_mac(pdata, ring->dma_desc_count - 1, FXGMAC_DMA_REG(channel, DMA_CH_RDRLR));
+
+	/* Update the starting address of descriptor ring */
+	desc_data = FXGMAC_GET_DESC_DATA(ring, start_index);
+	wr32_mac(pdata, upper_32_bits(desc_data->dma_desc_addr),
+		 FXGMAC_DMA_REG(channel, DMA_CH_RDLR_HI));
+	wr32_mac(pdata, lower_32_bits(desc_data->dma_desc_addr),
+		 FXGMAC_DMA_REG(channel, DMA_CH_RDLR_LO));
+
+	/* Update the Rx Descriptor Tail Pointer */
+	desc_data = FXGMAC_GET_DESC_DATA(ring, start_index +
+					       ring->dma_desc_count - 1);
+	wr32_mac(pdata, lower_32_bits(desc_data->dma_desc_addr),
+		 FXGMAC_DMA_REG(channel, DMA_CH_RDTR_LO));
+}
+
+static void fxgmac_desc_rx_init(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_desc_data *desc_data;
+	struct fxgmac_dma_desc *dma_desc;
+	struct fxgmac_channel *channel;
+	dma_addr_t dma_desc_addr;
+	struct fxgmac_ring *ring;
+
+	channel = pdata->channel_head;
+	for (u32 i = 0; i < pdata->channel_count; i++, channel++) {
+		ring = channel->rx_ring;
+		dma_desc = ring->dma_desc_head;
+		dma_desc_addr = ring->dma_desc_head_addr;
+
+		for (u32 j = 0; j < ring->dma_desc_count; j++) {
+			desc_data = FXGMAC_GET_DESC_DATA(ring, j);
+			desc_data->dma_desc = dma_desc;
+			desc_data->dma_desc_addr = dma_desc_addr;
+
+			if (fxgmac_rx_buffe_map(pdata, ring, desc_data))
+				break;
+
+			dma_desc++;
+			dma_desc_addr += sizeof(struct fxgmac_dma_desc);
+		}
+
+		ring->cur = 0;
+		ring->dirty = 0;
+
+		fxgmac_desc_rx_channel_init(channel);
+	}
+}
+
+static void fxgmac_desc_tx_channel_init(struct fxgmac_channel *channel)
+{
+	struct fxgmac_pdata *pdata = channel->pdata;
+	struct fxgmac_ring *ring = channel->tx_ring;
+	struct fxgmac_desc_data *desc_data;
+	int start_index = ring->cur;
+
+	/* Initialize all descriptors */
+	for (u32 i = 0; i < ring->dma_desc_count; i++) {
+		desc_data = FXGMAC_GET_DESC_DATA(ring, i);
+		fxgmac_desc_tx_reset(desc_data); /* Initialize Tx descriptor */
+	}
+
+	/* Update the total number of Tx descriptors */
+	wr32_mac(pdata, channel->pdata->tx_desc_count - 1,
+		 FXGMAC_DMA_REG(channel, DMA_CH_TDRLR));
+
+	/* Update the starting address of descriptor ring */
+	desc_data = FXGMAC_GET_DESC_DATA(ring, start_index);
+	wr32_mac(pdata, upper_32_bits(desc_data->dma_desc_addr),
+		 FXGMAC_DMA_REG(channel, DMA_CH_TDLR_HI));
+	wr32_mac(pdata, lower_32_bits(desc_data->dma_desc_addr),
+		 FXGMAC_DMA_REG(channel, DMA_CH_TDLR_LO));
+}
+
+static void fxgmac_desc_tx_init(struct fxgmac_pdata *pdata)
+{
+	struct fxgmac_desc_data *desc_data;
+	struct fxgmac_dma_desc *dma_desc;
+	struct fxgmac_channel *channel;
+	dma_addr_t dma_desc_addr;
+	struct fxgmac_ring *ring;
+
+	channel = pdata->channel_head;
+	channel->tx_timer_active = 0; /* reset the tx timer status. */
+	ring = channel->tx_ring;
+	dma_desc = ring->dma_desc_head;
+	dma_desc_addr = ring->dma_desc_head_addr;
+
+	for (u32 j = 0; j < ring->dma_desc_count; j++) {
+		desc_data = FXGMAC_GET_DESC_DATA(ring, j);
+		desc_data->dma_desc = dma_desc;
+		desc_data->dma_desc_addr = dma_desc_addr;
+
+		dma_desc++;
+		dma_desc_addr += sizeof(struct fxgmac_dma_desc);
+	}
+
+	ring->cur = 0;
+	ring->dirty = 0;
+	memset(&ring->tx, 0, sizeof(ring->tx));
+	fxgmac_desc_tx_channel_init(pdata->channel_head);
+}
+
+static int fxgmac_hw_init(struct fxgmac_pdata *pdata)
+{
+	int ret;
+
+	/* Flush Tx queues */
+	ret = fxgmac_flush_tx_queues(pdata);
+	if (ret < 0) {
+		yt_err(pdata, "%s, flush tx queue err:%d\n", __func__, ret);
+		return ret;
+	}
+
+	/* Initialize DMA related features */
+	fxgmac_config_dma_bus(pdata);
+	fxgmac_config_osp_mode(pdata);
+	fxgmac_config_pblx8(pdata);
+	fxgmac_config_tx_pbl_val(pdata);
+	fxgmac_config_rx_pbl_val(pdata);
+	fxgmac_config_rx_coalesce(pdata);
+	fxgmac_config_rx_buffer_size(pdata);
+	fxgmac_config_tso_mode(pdata);
+	fxgmac_config_sph_mode(pdata);
+	fxgmac_config_rss(pdata);
+
+	fxgmac_desc_tx_init(pdata);
+	fxgmac_desc_rx_init(pdata);
+	fxgmac_enable_dma_interrupts(pdata);
+
+	/* Initialize MTL related features */
+	fxgmac_config_mtl_mode(pdata);
+	fxgmac_config_queue_mapping(pdata);
+	fxgmac_config_tsf_mode(pdata, pdata->tx_sf_mode);
+	fxgmac_config_rsf_mode(pdata, pdata->rx_sf_mode);
+	fxgmac_config_tx_threshold(pdata, pdata->tx_threshold);
+	fxgmac_config_rx_threshold(pdata, pdata->rx_threshold);
+	fxgmac_config_tx_fifo_size(pdata);
+	fxgmac_config_rx_fifo_size(pdata);
+	fxgmac_config_flow_control_threshold(pdata);
+	fxgmac_config_rx_fep_disable(pdata);
+	fxgmac_config_rx_fup_enable(pdata);
+	fxgmac_enable_mtl_interrupts(pdata);
+
+	/* Initialize MAC related features */
+	fxgmac_config_mac_address(pdata);
+	fxgmac_config_crc_check(pdata);
+	fxgmac_config_rx_mode(pdata);
+	fxgmac_config_jumbo(pdata);
+	ret = fxgmac_config_flow_control(pdata);
+	if (ret < 0) {
+		yt_err(pdata, "%s, fxgmac_config_flow_control err:%d\n",
+		       __func__, ret);
+		return ret;
+	}
+
+	fxgmac_config_mac_speed(pdata);
+	fxgmac_config_checksum_offload(pdata);
+	fxgmac_config_vlan_support(pdata);
+	fxgmac_config_mmc(pdata);
+	fxgmac_enable_mac_interrupts(pdata);
+	fxgmac_config_wol_wait_time(pdata);
+
+	ret = fxgmac_phy_irq_enable(pdata, true);
+
+	if (netif_msg_drv(pdata))
+		yt_dbg(pdata, "%s ok\n", __func__);
+
+	return ret;
+}
+
+static void fxgmac_save_nonstick_reg(struct fxgmac_pdata *pdata)
+{
+	for (u32 i = GLOBAL_CTRL0; i < MSI_PBA; i += 4) {
+		pdata->reg_nonstick[(i - GLOBAL_CTRL0) >> 2] =
+			rd32_mem(pdata, i);
+	}
+}
+
+static void fxgmac_restore_nonstick_reg(struct fxgmac_pdata *pdata)
+{
+	for (u32 i = GLOBAL_CTRL0; i < MSI_PBA; i += 4)
+		wr32_mem(pdata, pdata->reg_nonstick[(i - GLOBAL_CTRL0) >> 2],
+			 i);
+}
+
+static void fxgmac_hw_exit(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0;
+
+	/* Issue a CHIP reset,
+	 * it will reset trigger circuit and reload efuse patch
+	 */
+	val = rd32_mem(pdata, SYS_RESET);
+	yt_dbg(pdata, "CHIP_RESET 0x%x\n", val);
+	fxgmac_set_bits(&val, SYS_RESET_POS, SYS_RESET_LEN, 1);
+	wr32_mem(pdata, val, SYS_RESET);
+	fsleep(9000);
+
+	fxgmac_phy_release(pdata);
+
+	/* Reset will clear nonstick registers. */
+	fxgmac_restore_nonstick_reg(pdata);
+}
+
+static void fxgmac_pcie_init(struct fxgmac_pdata *pdata)
+{
+	u32 val = 0;
+
+	fxgmac_set_bits(&val, LTR_IDLE_ENTER_REQUIRE_POS,
+			LTR_IDLE_ENTER_REQUIRE_LEN, LTR_IDLE_ENTER_REQUIRE);
+	fxgmac_set_bits(&val, LTR_IDLE_ENTER_SCALE_POS,
+			LTR_IDLE_ENTER_SCALE_LEN, LTR_IDLE_ENTER_SCALE_1024_NS);
+	fxgmac_set_bits(&val, LTR_IDLE_ENTER_POS, LTR_IDLE_ENTER_LEN,
+			LTR_IDLE_ENTER_900_US);
+	val = (val << 16) + val; /* snoopy + non-snoopy */
+	wr32_mem(pdata, val, LTR_IDLE_ENTER);
+
+	val = 0;
+	fxgmac_set_bits(&val, LTR_IDLE_EXIT_REQUIRE_POS,
+			LTR_IDLE_EXIT_REQUIRE_LEN, LTR_IDLE_EXIT_REQUIRE);
+	fxgmac_set_bits(&val, LTR_IDLE_EXIT_SCALE_POS, LTR_IDLE_EXIT_SCALE_LEN,
+			LTR_IDLE_EXIT_SCALE);
+	fxgmac_set_bits(&val, LTR_IDLE_EXIT_POS, LTR_IDLE_EXIT_LEN,
+			LTR_IDLE_EXIT_171_US);
+	val = (val << 16) + val; /* snoopy + non-snoopy */
+	wr32_mem(pdata, val, LTR_IDLE_EXIT);
+
+	val = 0;
+	fxgmac_set_bits(&val, PCIE_SERDES_PLL_AUTOOFF_POS,
+			PCIE_SERDES_PLL_AUTOOFF_LEN, 1);
+	wr32_mem(pdata, val, PCIE_SERDES_PLL);
+}
+
+static void fxgmac_clear_misc_int_status(struct fxgmac_pdata *pdata)
+{
+	u32 val, i;
+
+	if (fxgmac_phy_clear_interrupt(pdata) == 1)  /*  Link change */
+		phy_mac_interrupt(pdata->phydev);
+
+	/* Clear other interrupt status  */
+	val = rd32_mac(pdata, MAC_ISR);
+	if (val) {
+		if (val & BIT(MAC_ISR_PHYIF_STA_POS))
+			rd32_mac(pdata, MAC_PHYIF_STA);
+
+		if (val & (BIT(MAC_ISR_AN_SR0_POS) | BIT(MAC_ISR_AN_SR1_POS) |
+			   BIT(MAC_ISR_AN_SR2_POS)))
+			rd32_mac(pdata, MAC_AN_SR);
+
+		if (val & BIT(MAC_ISR_PMT_STA_POS))
+			rd32_mac(pdata, MAC_PMT_STA);
+
+		if (val & BIT(MAC_ISR_LPI_STA_POS))
+			rd32_mac(pdata, MAC_LPI_STA);
+
+		if (val & BIT(MAC_ISR_MMC_STA_POS)) {
+			if (val & BIT(MAC_ISR_RX_MMC_STA_POS))
+				fxgmac_rx_mmc_int(pdata);
+
+			if (val & BIT(MAC_ISR_TX_MMC_STA_POS))
+				fxgmac_tx_mmc_int(pdata);
+
+			if (val & BIT(MAC_ISR_IPCRXINT_POS))
+				rd32_mac(pdata, MMC_IPCRXINT);
+		}
+
+		if (val &
+		    (BIT(MAC_ISR_TX_RX_STA0_POS) | BIT(MAC_ISR_TX_RX_STA1_POS)))
+			rd32_mac(pdata, MAC_TX_RX_STA);
+
+		if (val & BIT(MAC_ISR_GPIO_SR_POS))
+			rd32_mac(pdata, MAC_GPIO_SR);
+	}
+
+	/* MTL_Interrupt_Status, write 1 clear */
+	val = rd32_mac(pdata, MTL_INT_SR);
+	if (val)
+		wr32_mac(pdata, val, MTL_INT_SR);
+
+	/* MTL_Q(#i)_Interrupt_Control_Status, write 1 clear */
+	for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++) {
+		/* Clear all the interrupts which are set */
+		val = rd32_mac(pdata, MTL_Q_INT_CTL_SR + i * MTL_Q_INC);
+		if (val)
+			wr32_mac(pdata, val, MTL_Q_INT_CTL_SR + i * MTL_Q_INC);
+	}
+
+	/* MTL_ECC_Interrupt_Status, write 1 clear */
+	val = rd32_mac(pdata, MTL_ECC_INT_SR);
+	if (val)
+		wr32_mac(pdata, val, MTL_ECC_INT_SR);
+
+	/* DMA_ECC_Interrupt_Status, write 1 clear */
+	val = rd32_mac(pdata, DMA_ECC_INT_SR);
+	if (val)
+		wr32_mac(pdata, val, DMA_ECC_INT_SR);
+}
+
+void fxgmac_hw_ops_init(struct fxgmac_hw_ops *hw_ops)
+{
+	hw_ops->pcie_init = fxgmac_pcie_init;
+	hw_ops->init = fxgmac_hw_init;
+	hw_ops->exit = fxgmac_hw_exit;
+	hw_ops->save_nonstick_reg = fxgmac_save_nonstick_reg;
+	hw_ops->restore_nonstick_reg = fxgmac_restore_nonstick_reg;
+
+	hw_ops->enable_tx = fxgmac_enable_tx;
+	hw_ops->disable_tx = fxgmac_disable_tx;
+	hw_ops->enable_rx = fxgmac_enable_rx;
+	hw_ops->disable_rx = fxgmac_disable_rx;
+
+	hw_ops->enable_channel_irq = fxgmac_enable_channel_irq;
+	hw_ops->disable_channel_irq = fxgmac_disable_channel_irq;
+	hw_ops->set_interrupt_moderation = fxgmac_set_interrupt_moderation;
+	hw_ops->enable_msix_one_irq = fxgmac_enable_msix_one_irq;
+	hw_ops->disable_msix_one_irq = fxgmac_disable_msix_one_irq;
+	hw_ops->enable_mgm_irq = fxgmac_enable_mgm_irq;
+	hw_ops->disable_mgm_irq = fxgmac_disable_mgm_irq;
+
+	hw_ops->dismiss_all_int = fxgmac_dismiss_all_int;
+	hw_ops->clear_misc_int_status = fxgmac_clear_misc_int_status;
+
+	hw_ops->set_mac_address = fxgmac_set_mac_address;
+	hw_ops->set_mac_hash = fxgmac_set_mc_addresses;
+	hw_ops->config_rx_mode = fxgmac_config_rx_mode;
+	hw_ops->enable_rx_csum = fxgmac_enable_rx_csum;
+	hw_ops->disable_rx_csum = fxgmac_disable_rx_csum;
+	hw_ops->config_tso = fxgmac_config_tso_mode;
+	hw_ops->calculate_max_checksum_size =
+		fxgmac_calculate_max_checksum_size;
+	hw_ops->config_mac_speed = fxgmac_config_mac_speed;
+
+	/* PHY Control */
+	hw_ops->reset_phy = fxgmac_phy_reset;
+	hw_ops->release_phy = fxgmac_phy_release;
+	hw_ops->write_phy_reg = fxgmac_phy_write_reg;
+	hw_ops->read_phy_reg = fxgmac_phy_read_reg;
+
+	/* Vlan related config */
+	hw_ops->enable_rx_vlan_stripping = fxgmac_enable_rx_vlan_stripping;
+	hw_ops->disable_rx_vlan_stripping = fxgmac_disable_rx_vlan_stripping;
+	hw_ops->enable_rx_vlan_filtering = fxgmac_enable_rx_vlan_filtering;
+	hw_ops->disable_rx_vlan_filtering = fxgmac_disable_rx_vlan_filtering;
+	hw_ops->update_vlan_hash_table = fxgmac_update_vlan_hash_table;
+
+	/* RX coalescing */
+	hw_ops->config_rx_coalesce = fxgmac_config_rx_coalesce;
+	hw_ops->usec_to_riwt = fxgmac_usec_to_riwt;
+	/* Receive Side Scaling */
+	hw_ops->enable_rss = fxgmac_enable_rss;
+	hw_ops->disable_rss = fxgmac_disable_rss;
+	hw_ops->get_rss_options = fxgmac_read_rss_options;
+	hw_ops->set_rss_options = fxgmac_write_rss_options;
+	hw_ops->set_rss_hash_key = fxgmac_set_rss_hash_key;
+	hw_ops->write_rss_lookup_table = fxgmac_write_rss_lookup_table;
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2024-11-20 11:14 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-20 10:56 [PATCH net-next v2 00/21] net:yt6801: Add Motorcomm yt6801 PCIe driver Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 03/21] motorcomm:yt6801: Implement the fxgmac_drv_probe function Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 04/21] motorcomm:yt6801: Implement the .ndo_open function Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 05/21] motorcomm:yt6801: Implement the fxgmac_start function Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 08/21] motorcomm:yt6801: Implement the fxgmac_read_mac_addr function Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 12/21] motorcomm:yt6801: Implement .ndo_tx_timeout and .ndo_change_mtu functions Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 13/21] motorcomm:yt6801: Implement some ethtool_ops function Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 14/21] motorcomm:yt6801: Implement the WOL function of ethtool_ops Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 15/21] motorcomm:yt6801: Implement pci_driver suspend and resume Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 16/21] motorcomm:yt6801: Add a Makefile in the motorcomm folder Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 17/21] motorcomm:yt6801: Update the Makefile and Kconfig in the motorcomm Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 18/21] motorcomm:yt6801: Update the Makefile and Kconfig in the ethernet Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 19/21] ethernet: Update the index.rst in the ethernet documentation folder Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 20/21] motorcomm:yt6801: Add a yt6801.rst " Frank Sae
2024-11-20 10:56 ` [PATCH net-next v2 21/21] MAINTAINERS:Add the motorcomm ethernet driver entry Frank Sae
2024-11-20 11:14 ` [PATCH net-next v2 11/21] motorcomm:yt6801: Implement some net_device_ops function Frank Sae
2024-11-20 11:14 ` [PATCH net-next v2 09/21] motorcomm:yt6801: Implement some hw_ops function Frank Sae

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox