[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250228100020.3944-4-Frank.Sae@motor-comm.com>
Date: Fri, 28 Feb 2025 18:00:09 +0800
From: Frank Sae <Frank.Sae@...or-comm.com>
To: Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>,
Andrew Lunn <andrew@...n.ch>,
Heiner Kallweit <hkallweit1@...il.com>,
Russell King <linux@...linux.org.uk>,
"David S . Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Frank <Frank.Sae@...or-comm.com>,
netdev@...r.kernel.org
Cc: Masahiro Yamada <masahiroy@...nel.org>,
Parthiban.Veerasooran@...rochip.com,
linux-kernel@...r.kernel.org,
xiaogang.fan@...or-comm.com,
fei.zhang@...or-comm.com,
hua.sun@...or-comm.com
Subject: [PATCH net-next v3 03/14] motorcomm:yt6801: Implement pci_driver shutdown
Implement the pci_driver shutdown function to shutdown this driver.
Implement the fxgmac_net_powerdown function to stop tx, disable tx,
disable rx, config powerdown, free rx date and free tx date.
Signed-off-by: Frank Sae <Frank.Sae@...or-comm.com>
---
.../ethernet/motorcomm/yt6801/yt6801_desc.c | 50 +++
.../ethernet/motorcomm/yt6801/yt6801_desc.h | 35 ++
.../ethernet/motorcomm/yt6801/yt6801_net.c | 301 ++++++++++++++++++
.../ethernet/motorcomm/yt6801/yt6801_pci.c | 24 ++
4 files changed, 410 insertions(+)
create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c
create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.h
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c
new file mode 100644
index 000000000..3ff5eff11
--- /dev/null
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2022 - 2024 Motorcomm Electronic Technology Co.,Ltd. */
+
+#include "yt6801.h"
+#include "yt6801_desc.h"
+
+void fxgmac_desc_data_unmap(struct fxgmac_pdata *priv,
+ struct fxgmac_desc_data *desc_data)
+{
+ if (desc_data->skb_dma) {
+ if (desc_data->mapped_as_page) {
+ dma_unmap_page(priv->dev, desc_data->skb_dma,
+ desc_data->skb_dma_len, DMA_TO_DEVICE);
+ } else {
+ dma_unmap_single(priv->dev, desc_data->skb_dma,
+ desc_data->skb_dma_len, DMA_TO_DEVICE);
+ }
+ desc_data->skb_dma = 0;
+ desc_data->skb_dma_len = 0;
+ }
+
+ if (desc_data->skb) {
+ dev_kfree_skb_any(desc_data->skb);
+ desc_data->skb = NULL;
+ }
+
+ if (desc_data->rx.hdr.pa.pages)
+ put_page(desc_data->rx.hdr.pa.pages);
+
+ if (desc_data->rx.hdr.pa_unmap.pages) {
+ dma_unmap_page(priv->dev, desc_data->rx.hdr.pa_unmap.pages_dma,
+ desc_data->rx.hdr.pa_unmap.pages_len,
+ DMA_FROM_DEVICE);
+ put_page(desc_data->rx.hdr.pa_unmap.pages);
+ }
+
+ if (desc_data->rx.buf.pa.pages)
+ put_page(desc_data->rx.buf.pa.pages);
+
+ if (desc_data->rx.buf.pa_unmap.pages) {
+ dma_unmap_page(priv->dev, desc_data->rx.buf.pa_unmap.pages_dma,
+ desc_data->rx.buf.pa_unmap.pages_len,
+ DMA_FROM_DEVICE);
+ put_page(desc_data->rx.buf.pa_unmap.pages);
+ }
+ memset(&desc_data->tx, 0, sizeof(desc_data->tx));
+ memset(&desc_data->rx, 0, sizeof(desc_data->rx));
+
+ desc_data->mapped_as_page = 0;
+}
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.h b/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.h
new file mode 100644
index 000000000..b238f20be
--- /dev/null
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2022 - 2024 Motorcomm Electronic Technology Co.,Ltd. */
+
+#ifndef YT6801_DESC_H
+#define YT6801_DESC_H
+
+#define FXGMAC_TX_DESC_CNT 256
+#define FXGMAC_TX_DESC_MIN_FREE (FXGMAC_TX_DESC_CNT >> 3)
+#define FXGMAC_TX_DESC_MAX_PROC (FXGMAC_TX_DESC_CNT >> 1)
+#define FXGMAC_RX_DESC_CNT 1024
+#define FXGMAC_RX_DESC_MAX_DIRTY (FXGMAC_RX_DESC_CNT >> 3)
+
+#define FXGMAC_GET_DESC_DATA(ring, idx) ((ring)->desc_data_head + (idx))
+#define FXGMAC_GET_ENTRY(x, size) (((x) + 1) & ((size) - 1))
+
+void fxgmac_desc_tx_reset(struct fxgmac_desc_data *desc_data);
+void fxgmac_desc_rx_reset(struct fxgmac_desc_data *desc_data);
+void fxgmac_desc_data_unmap(struct fxgmac_pdata *priv,
+ struct fxgmac_desc_data *desc_data);
+
+int fxgmac_channels_rings_alloc(struct fxgmac_pdata *priv);
+void fxgmac_channels_rings_free(struct fxgmac_pdata *priv);
+int fxgmac_tx_skb_map(struct fxgmac_channel *channel, struct sk_buff *skb);
+int fxgmac_rx_buffe_map(struct fxgmac_pdata *priv, struct fxgmac_ring *ring,
+ struct fxgmac_desc_data *desc_data);
+void fxgmac_dump_tx_desc(struct fxgmac_pdata *priv, struct fxgmac_ring *ring,
+ unsigned int idx, unsigned int count,
+ unsigned int flag);
+void fxgmac_dump_rx_desc(struct fxgmac_pdata *priv, struct fxgmac_ring *ring,
+ unsigned int idx);
+
+int fxgmac_is_tx_complete(struct fxgmac_dma_desc *dma_desc);
+int fxgmac_is_last_desc(struct fxgmac_dma_desc *dma_desc);
+
+#endif /* YT6801_DESC_H */
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
index c54550cd4..7d557f6b0 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c
@@ -9,6 +9,7 @@
#include <linux/tcp.h>
#include "yt6801.h"
+#include "yt6801_desc.h"
#define PHY_WR_CONFIG(reg_offset) (0x8000205 + ((reg_offset) * 0x10000))
static int fxgmac_phy_write_reg(struct fxgmac_pdata *priv, u32 reg_id, u32 data)
@@ -98,6 +99,229 @@ static int fxgmac_mdio_register(struct fxgmac_pdata *priv)
return 0;
}
+static void fxgmac_disable_mgm_irq(struct fxgmac_pdata *priv)
+{
+ FXGMAC_IO_WR_BITS(priv, MGMT_INT_CTRL0, INT_MASK,
+ MGMT_INT_CTRL0_INT_MASK_MASK);
+}
+
+static void napi_disable_del(struct fxgmac_pdata *priv, struct napi_struct *n,
+ u32 flag_pos)
+{
+ napi_disable(n);
+ netif_napi_del(n);
+ SET_BITS(priv->int_flag, flag_pos, 1, 0); /* set flag_pos bit to 0 */
+}
+
+static void fxgmac_napi_disable(struct fxgmac_pdata *priv)
+{
+ u32 rx = FXGMAC_GET_BITS(priv->int_flag, INT_FLAG, RX_NAPI);
+ struct fxgmac_channel *channel = priv->channel_head;
+
+ if (!priv->per_channel_irq) {
+ if (!FXGMAC_GET_BITS(priv->int_flag, INT_FLAG, LEGACY_NAPI))
+ return;
+
+ napi_disable_del(priv, &priv->napi,
+ INT_FLAG_LEGACY_NAPI_POS);
+ return;
+ }
+
+ if (FXGMAC_GET_BITS(priv->int_flag, INT_FLAG, TX_NAPI))
+ napi_disable_del(priv, &channel->napi_tx,
+ INT_FLAG_TX_NAPI_POS);
+
+ for (u32 i = 0; i < priv->channel_count; i++, channel++)
+ if (GET_BITS(rx, i, INT_FLAG_PER_RX_NAPI_LEN))
+ napi_disable_del(priv, &channel->napi_rx,
+ INT_FLAG_RX_NAPI_POS + i);
+}
+
+static void fxgmac_free_irqs(struct fxgmac_pdata *priv)
+{
+ u32 i, rx = FXGMAC_GET_BITS(priv->int_flag, INT_FLAG, RX_IRQ);
+ struct fxgmac_channel *channel = priv->channel_head;
+
+ if (!FXGMAC_GET_BITS(priv->int_flag, INT_FLAG, MSIX) &&
+ FXGMAC_GET_BITS(priv->int_flag, INT_FLAG, LEGACY_IRQ)) {
+ devm_free_irq(priv->dev, priv->dev_irq, priv);
+ FXGMAC_SET_BITS(priv->int_flag, INT_FLAG, LEGACY_IRQ, 0);
+ }
+
+ if (!priv->per_channel_irq)
+ return;
+
+ if (FXGMAC_GET_BITS(priv->int_flag, INT_FLAG, TX_IRQ)) {
+ FXGMAC_SET_BITS(priv->int_flag, INT_FLAG, TX_IRQ, 0);
+ devm_free_irq(priv->dev, channel->dma_irq_tx, channel);
+ }
+
+ for (i = 0; i < priv->channel_count; i++, channel++) {
+ if (GET_BITS(rx, i, INT_FLAG_PER_RX_IRQ_LEN)) {
+ SET_BITS(priv->int_flag, INT_FLAG_RX_IRQ_POS + i,
+ INT_FLAG_PER_RX_IRQ_LEN, 0);
+ devm_free_irq(priv->dev, channel->dma_irq_rx, channel);
+ }
+ }
+}
+
+static void fxgmac_free_tx_data(struct fxgmac_pdata *priv)
+{
+ struct fxgmac_channel *channel = priv->channel_head;
+ struct fxgmac_ring *ring;
+
+ for (u32 i = 0; i < priv->channel_count; i++, channel++) {
+ ring = channel->tx_ring;
+ if (!ring)
+ break;
+
+ for (u32 j = 0; j < ring->dma_desc_count; j++)
+ fxgmac_desc_data_unmap(priv,
+ FXGMAC_GET_DESC_DATA(ring, j));
+ }
+}
+
+static void fxgmac_free_rx_data(struct fxgmac_pdata *priv)
+{
+ struct fxgmac_channel *channel = priv->channel_head;
+ struct fxgmac_ring *ring;
+
+ for (u32 i = 0; i < priv->channel_count; i++, channel++) {
+ ring = channel->rx_ring;
+ if (!ring)
+ break;
+
+ for (u32 j = 0; j < ring->dma_desc_count; j++)
+ fxgmac_desc_data_unmap(priv,
+ FXGMAC_GET_DESC_DATA(ring, j));
+ }
+}
+
+static void fxgmac_prepare_tx_stop(struct fxgmac_pdata *priv,
+ 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 = FXGMAC_MAC_IO_RD(priv, tx_dsr);
+ tx_status = 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_err(priv,
+ "timed out waiting for Tx DMA channel %u to stop\n",
+ channel->queue_index);
+}
+
+static void fxgmac_disable_tx(struct fxgmac_pdata *priv)
+{
+ struct fxgmac_channel *channel = priv->channel_head;
+
+ /* Prepare for Tx DMA channel stop */
+ fxgmac_prepare_tx_stop(priv, channel);
+
+ FXGMAC_MAC_IO_WR_BITS(priv, MAC_CR, TE, 0);/* Disable MAC Tx */
+
+ /* Disable Tx queue */
+ FXGMAC_MTL_IO_WR_BITS(priv, 0, MTL_Q_TQOMR, TXQEN, MTL_Q_DISABLED);
+
+ /* Disable Tx DMA channel */
+ FXGMAC_DMA_IO_WR_BITS(channel, DMA_CH_TCR, ST, 0);
+}
+
+static void fxgmac_prepare_rx_stop(struct fxgmac_pdata *priv,
+ 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 = FXGMAC_MTL_IO_RD(priv, queue, MTL_Q_RQDR);
+ rx_q = FXGMAC_GET_BITS(rx_status, MTL_Q_RQDR, PRXQ);
+ rx_q_sts = FXGMAC_GET_BITS(rx_status, MTL_Q_RQDR, RXQSTS);
+ if (rx_q == 0 && rx_q_sts == 0)
+ break;
+
+ fsleep(500);
+ }
+
+ if (!time_before(jiffies, rx_timeout))
+ yt_err(priv, "timed out waiting for Rx queue %u to empty\n",
+ queue);
+}
+
+static void fxgmac_disable_rx(struct fxgmac_pdata *priv)
+{
+ struct fxgmac_channel *channel = priv->channel_head;
+ u32 i;
+
+ /* Disable MAC Rx */
+ FXGMAC_MAC_IO_WR_BITS(priv, MAC_CR, CST, 0);
+ FXGMAC_MAC_IO_WR_BITS(priv, MAC_CR, ACS, 0);
+ FXGMAC_MAC_IO_WR_BITS(priv, MAC_CR, RE, 0);
+
+ /* Prepare for Rx DMA channel stop */
+ for (i = 0; i < priv->rx_q_count; i++)
+ fxgmac_prepare_rx_stop(priv, i);
+
+ FXGMAC_MAC_IO_WR(priv, MAC_RQC0R, 0); /* Disable each Rx queue */
+
+ /* Disable each Rx DMA channel */
+ for (i = 0; i < priv->channel_count; i++, channel++)
+ FXGMAC_DMA_IO_WR_BITS(channel, DMA_CH_RCR, SR, 0);
+}
+
+/**
+ * fxgmac_set_oob_wol - disable or enable oob wol crtl function
+ * @priv: driver private struct
+ * @enable: 1 or 0
+ *
+ * Description: 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.
+ */
+static void fxgmac_set_oob_wol(struct fxgmac_pdata *priv, unsigned int en)
+{
+ FXGMAC_IO_WR_BITS(priv, OOB_WOL_CTRL, DIS, !en);/* en = 1 is disable */
+}
+
+static void fxgmac_pre_powerdown(struct fxgmac_pdata *priv)
+{
+ fxgmac_set_oob_wol(priv, 1);
+ fsleep(2000);
+}
+
static void fxgmac_phy_release(struct fxgmac_pdata *priv)
{
FXGMAC_IO_WR_BITS(priv, EPHY_CTRL, RESET, 1);
@@ -110,6 +334,83 @@ void fxgmac_phy_reset(struct fxgmac_pdata *priv)
fsleep(1500);
}
+static void fxgmac_disable_msix_irqs(struct fxgmac_pdata *priv)
+{
+ for (u32 intid = 0; intid < MSIX_TBL_MAX_NUM; intid++)
+ fxgmac_disable_msix_one_irq(priv, intid);
+}
+
+static void fxgmac_stop(struct fxgmac_pdata *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ struct netdev_queue *txq;
+
+ if (priv->dev_state != FXGMAC_DEV_START)
+ return;
+
+ priv->dev_state = FXGMAC_DEV_STOP;
+
+ if (priv->per_channel_irq)
+ fxgmac_disable_msix_irqs(priv);
+ else
+ fxgmac_disable_mgm_irq(priv);
+
+ netif_carrier_off(netdev);
+ netif_tx_stop_all_queues(netdev);
+ fxgmac_disable_tx(priv);
+ fxgmac_disable_rx(priv);
+ fxgmac_free_irqs(priv);
+ fxgmac_napi_disable(priv);
+ phy_stop(priv->phydev);
+
+ txq = netdev_get_tx_queue(netdev, priv->channel_head->queue_index);
+ netdev_tx_reset_queue(txq);
+}
+
+static void fxgmac_config_powerdown(struct fxgmac_pdata *priv)
+{
+ FXGMAC_MAC_IO_WR_BITS(priv, MAC_CR, RE, 1); /* Enable MAC Rx */
+ FXGMAC_MAC_IO_WR_BITS(priv, MAC_CR, TE, 1); /* Enable MAC TX */
+
+ /* Set GAMC power down */
+ FXGMAC_MAC_IO_WR_BITS(priv, MAC_PMT_STA, PWRDWN, 1);
+}
+
+int fxgmac_net_powerdown(struct fxgmac_pdata *priv)
+{
+ struct net_device *netdev = priv->netdev;
+
+ /* Signal that we are down to the interrupt handler */
+ if (__test_and_set_bit(FXGMAC_POWER_STATE_DOWN, &priv->powerstate))
+ return 0; /* do nothing if already down */
+
+ __clear_bit(FXGMAC_POWER_STATE_UP, &priv->powerstate);
+ netif_tx_stop_all_queues(netdev); /* Shut off incoming Tx traffic */
+
+ /* Call carrier off first to avoid false dev_watchdog timeouts */
+ netif_carrier_off(netdev);
+ netif_tx_disable(netdev);
+ fxgmac_disable_rx(priv);
+
+ /* Synchronize_rcu() needed for pending XDP buffers to drain */
+ synchronize_rcu();
+
+ fxgmac_stop(priv);
+ fxgmac_pre_powerdown(priv);
+
+ if (!test_bit(FXGMAC_POWER_STATE_DOWN, &priv->powerstate))
+ yt_err(priv,
+ "fxgmac powerstate is %lu when config powe down.\n",
+ priv->powerstate);
+
+ /* Set mac to lowpower mode */
+ fxgmac_config_powerdown(priv);
+ fxgmac_free_tx_data(priv);
+ fxgmac_free_rx_data(priv);
+
+ return 0;
+}
+
#ifdef CONFIG_PCI_MSI
static void fxgmac_init_interrupt_scheme(struct fxgmac_pdata *priv)
{
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c
index 1b80ae15a..fba01e393 100644
--- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c
+++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c
@@ -80,6 +80,29 @@ static void fxgmac_remove(struct pci_dev *pcidev)
dev_dbg(dev, "%s has been removed\n", netdev->name);
}
+static void __fxgmac_shutdown(struct pci_dev *pcidev)
+{
+ struct fxgmac_pdata *priv = dev_get_drvdata(&pcidev->dev);
+ struct net_device *netdev = priv->netdev;
+
+ rtnl_lock();
+ fxgmac_net_powerdown(priv);
+ netif_device_detach(netdev);
+ rtnl_unlock();
+}
+
+static void fxgmac_shutdown(struct pci_dev *pcidev)
+{
+ struct fxgmac_pdata *priv = dev_get_drvdata(&pcidev->dev);
+
+ mutex_lock(&priv->mutex);
+ __fxgmac_shutdown(pcidev);
+ if (system_state == SYSTEM_POWER_OFF) {
+ pci_wake_from_d3(pcidev, false);
+ pci_set_power_state(pcidev, PCI_D3hot);
+ }
+ mutex_unlock(&priv->mutex);
+}
#define MOTORCOMM_PCI_ID 0x1f0a
#define YT6801_PCI_DEVICE_ID 0x6801
@@ -95,6 +118,7 @@ static struct pci_driver fxgmac_pci_driver = {
.id_table = fxgmac_pci_tbl,
.probe = fxgmac_probe,
.remove = fxgmac_remove,
+ .shutdown = fxgmac_shutdown,
};
module_pci_driver(fxgmac_pci_driver);
--
2.34.1
Powered by blists - more mailing lists