summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerge Semin <fancer.lancer@gmail.com>2025-09-12 14:08:44 +0300
committerSerge Semin <fancer.lancer@gmail.com>2026-08-13 20:36:54 +0300
commit995f7a1b5ff5b86db25d2cd484cf565bce7b1e65 (patch)
treeef49f5652024124afdfd8862220d03f272c53931
parenta8c19a421893fca887060cd525f3e86371fd1801 (diff)
downloadlinux-gmac/ext4/gpio.tar.gz
linux-gmac/ext4/gpio.zip
net: stmmac: dwmac1000: Add GPIO module supportgmac/ext4/gpio
Synopsys DW GMAC v3.7x can be synthesized with up to four GPIs and four GPOs support, which in case if enabled can be configured via a MAC CSR 0xe0. In order to have the DW GMAC GPIO interface supported in the STMMAC GPIO driver the GPIO CSR accessors and MAC/DMA gentle stop/cleanup methods are introduced. These are utilized as the low-level feature implementation abstracting out the DW GMAC GPIO-specifics. The GPIO-chip related code using that infrastructure has already been added to the driver. Signed-off-by: Serge Semin <fancer.lancer@gmail.com>
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h14
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c162
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c24
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.c1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.h1
5 files changed, 202 insertions, 0 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index 809a09a2a4d4..1de5d07b64ad 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -27,6 +27,7 @@
#define GMAC_INT_STATUS_MMCTIS BIT(6)
#define GMAC_INT_STATUS_MMCCSUM BIT(7)
#define GMAC_INT_STATUS_LPIIS BIT(10)
+#define GMAC_INT_STATUS_GPIIS BIT(11)
/* interrupt mask register */
#define GMAC_INT_MASK 0x0000003c
@@ -75,6 +76,14 @@ enum power_event {
/* GMAC Watchdog timeout for received frames register */
#define GMAC_WDT 0x000000dc
+/* General Purpose IO register */
+#define GMAC_GPIO 0x000000e0 /* General Purpose IO */
+
+/* MMC registers */
+#define GMAC_MMC_CTRL 0x00000100
+#define GMAC_MMC_RX_INTR 0x00000104
+#define GMAC_MMC_TX_INTR 0x00000108
+
/* VLAN Tag Inclusion or Replacement register */
#define GMAC_VLAN_INCL 0x00000584
@@ -137,6 +146,11 @@ enum inter_frame_gap {
/* Watchdog timeout for received frames defines */
#define GMAC_WDT_PWE BIT(16)
#define GMAC_WDT_WTO GENMASK(13, 0)
+/* General Purpose IO defines */
+#define GMAC_GPIO_GPIS GENMASK(3, 0) /* General Purpose Input Sts */
+#define GMAC_GPIO_GPO GENMASK(11, 8) /* General Purpose Output */
+#define GMAC_GPIO_GPIE GENMASK(19, 16) /* GPI Interrupt Enable */
+#define GMAC_GPIO_GPIT GENMASK(27, 24) /* GPI Type */
/* VLAN Tag inclusing defines */
#define GMAC_VLAN_CSVL BIT(19)
#define GMAC_VLAN_VLC GENMASK(17, 16)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index 29fb465d8192..31fbaa93aa2e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -12,6 +12,7 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#include <linux/bitfield.h>
#include <linux/crc32.h>
#include <linux/slab.h>
#include <linux/ethtool.h>
@@ -22,6 +23,7 @@
#include "stmmac_pcs.h"
#include "stmmac_ptp.h"
#include "dwmac1000.h"
+#include "dwmac_dma.h"
static int dwmac1000_pcs_init(struct stmmac_priv *priv)
{
@@ -86,6 +88,125 @@ static void dwmac1000_irq_modify(struct mac_device_info *hw, u32 disable,
spin_unlock_irqrestore(&hw->irq_ctrl_lock, flags);
}
+/* Stop the DMA and MAC engines (as described in the Stopping and
+ * Starting Transmission chapter of the databook)
+ */
+static int dwmac1000_core_stop(struct mac_device_info *hw)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ const unsigned int maxch = 8;
+ int ret, ch;
+ u32 value;
+
+ /* Disable and clear DMA IRQ */
+ for (ch = 0; ch < maxch; ch++) {
+ writel(0, ioaddr + DMA_CHAN_INTR_ENA(ch));
+ writel(0x0001FFFF, ioaddr + DMA_CHAN_STATUS(ch));
+ }
+
+ /* Disable MAC interrupts. Note the MAC interrupts are enabled by
+ * default after reset, but mask them out anyway so to avoid any
+ * spurious IRQ in the clean up procedure
+ */
+ writel(0x7FF, ioaddr + GMAC_INT_MASK);
+
+ /* Disable DMA transmitter */
+ for (ch = 0; ch < maxch; ch++) {
+ value = readl(ioaddr + DMA_CHAN_CONTROL(ch));
+ value &= ~DMA_CONTROL_ST;
+ writel(value, ioaddr + DMA_CHAN_CONTROL(ch));
+ }
+
+ /* Wait for MAC to stop transmitting the data */
+ ret = readl_poll_timeout(ioaddr + GMAC_DEBUG, value,
+ !(value & GMAC_DEBUG_TPESTS),
+ 1000, 20000);
+ if (ret)
+ return ret;
+
+ /* Disable MAC transmitter and receiver */
+ writel(0, ioaddr + GMAC_CONTROL);
+
+ /* Make sure Tx FIFOs is empty */
+ ret = readl_poll_timeout(ioaddr + GMAC_DEBUG, value,
+ !(value & GMAC_DEBUG_TXFSTS),
+ 1000, 20000);
+ if (ret)
+ return ret;
+
+ /* Wait for MAC to stop receiving the data */
+ ret = readl_poll_timeout(ioaddr + GMAC_DEBUG, value,
+ !(value & GMAC_DEBUG_RPESTS),
+ 1000, 20000);
+ if (ret)
+ return ret;
+
+ /* Make sure Rx FIFOs is empty */
+ ret = readl_poll_timeout(ioaddr + GMAC_DEBUG, value,
+ !(value & GMAC_DEBUG_RXFSTS_MASK),
+ 1000, 20000);
+ if (ret)
+ return ret;
+
+ /* Disable DMA receiver after making sure no data in Rx FIFO */
+ for (ch = 0; ch < maxch; ch++)
+ writel(0, ioaddr + DMA_CHAN_CONTROL(ch));
+
+ return 0;
+}
+
+/* Clean the basic MAC registers up */
+static void dwmac1000_core_clean(struct mac_device_info *hw)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ int i;
+
+ /* Restore default CSRs state */
+ writel(0, ioaddr + GMAC_FRAME_FILTER);
+ writel(0, ioaddr + GMAC_HASH_HIGH);
+ writel(0, ioaddr + GMAC_HASH_LOW);
+ for (i = 0; i < 8; i++)
+ writel(0, ioaddr + GMAC_EXTHASH_BASE + 4 * i);
+ writel(0, ioaddr + GMAC_FLOW_CTRL);
+ writel(0, ioaddr + GMAC_VLAN_TAG);
+ writel(0x80000000, ioaddr + GMAC_PMT);
+ writel(0, ioaddr + LPI_CTRL_STATUS);
+ writel(0x03e80000, ioaddr + LPI_TIMER_CTRL);
+ for (i = 0; i < 128; i++) {
+ writel(0x0000ffff, ioaddr + GMAC_ADDR_HIGH(i));
+ writel(0xffffffff, ioaddr + GMAC_ADDR_LOW(i));
+ }
+ writel(0, ioaddr + GMAC_PCS_BASE);
+ writel(0, ioaddr + GMAC_RGSMIIIS);
+ writel(0, ioaddr + GMAC_WDT);
+ writel(0x1, ioaddr + GMAC_MMC_CTRL);
+ writel(0, ioaddr + GMAC_VLAN_INCL);
+ writel(0, ioaddr + GMAC_VLAN_HASH_TABLE);
+
+ /* IEEE 1588-2002/2008 Timestamps */
+ writel(0, ioaddr + 0xb00);
+ writel(0, ioaddr + 0xb04);
+ writel(0, ioaddr + 0xb10);
+ writel(0, ioaddr + 0xb14);
+ writel(0, ioaddr + 0xb18);
+
+ /* L3/L4 filters (not supported by the driver yet) */
+ writel(0, ioaddr + 0x400);
+ writel(0, ioaddr + 0x430);
+ writel(0, ioaddr + 0x404);
+ writel(0, ioaddr + 0x434);
+ for (i = 0; i < 4; i++)
+ writel(0, ioaddr + 0x410 + 4 * i);
+
+ /* Clear the pending statuses */
+ readl(ioaddr + GMAC_INT_STATUS);
+ readl(ioaddr + GMAC_PMT);
+ readl(ioaddr + LPI_CTRL_STATUS);
+ readl(ioaddr + GMAC_RGSMIIIS);
+ readl(ioaddr + GMAC_MMC_RX_INTR);
+ readl(ioaddr + GMAC_MMC_TX_INTR);
+}
+
static void dwmac1000_rx_fcs_enable(struct mac_device_info *hw, bool enable)
{
void __iomem *ioaddr = hw->pcsr;
@@ -680,9 +801,44 @@ static void dwmac1000_set_mac_loopback(void __iomem *ioaddr, bool enable)
writel(value, ioaddr + GMAC_CONTROL);
}
+static void dwmac1000_gpio_set_ctrl(struct stmmac_priv *priv, u32 gpie,
+ u32 gpit, u32 gpo)
+{
+ u32 value;
+
+ value = FIELD_PREP(GMAC_GPIO_GPO, gpo) |
+ FIELD_PREP(GMAC_GPIO_GPIE, gpie) |
+ FIELD_PREP(GMAC_GPIO_GPIT, gpit);
+
+ writel(value, priv->ioaddr + GMAC_GPIO);
+}
+
+static void dwmac1000_gpio_get_ctrl(struct stmmac_priv *priv, u32 *gpie,
+ u32 *gpit, u32 *gpo)
+{
+ u32 value;
+
+ value = readl(priv->ioaddr + GMAC_GPIO);
+
+ *gpie = FIELD_GET(GMAC_GPIO_GPIE, value);
+ *gpit = FIELD_GET(GMAC_GPIO_GPIT, value);
+ *gpo = FIELD_GET(GMAC_GPIO_GPO, value);
+}
+
+static int dwmac1000_gpio_get_gpis(struct stmmac_priv *priv)
+{
+ u32 value;
+
+ value = readl(priv->ioaddr + GMAC_GPIO);
+
+ return FIELD_GET(GMAC_GPIO_GPIS, value);
+}
+
const struct stmmac_ops dwmac1000_ops = {
.pcs_init = dwmac1000_pcs_init,
.core_init = dwmac1000_core_init,
+ .core_stop = dwmac1000_core_stop,
+ .core_clean = dwmac1000_core_clean,
.irq_modify = dwmac1000_irq_modify,
.set_mac = stmmac_set_mac,
.rx_fcs = dwmac1000_rx_fcs_enable,
@@ -712,6 +868,12 @@ const struct stmmac_vlan_ops dwmac1000_vlan_ops = {
.set_hw_vlan_mode = dwmac1000_vlan_set_hw_mode,
};
+const struct stmmac_gpio_ops dwmac1000_gpio_ops = {
+ .set_ctrl = dwmac1000_gpio_set_ctrl,
+ .get_ctrl = dwmac1000_gpio_get_ctrl,
+ .get_gpis = dwmac1000_gpio_get_gpis,
+};
+
int dwmac1000_setup(struct stmmac_priv *priv)
{
struct mac_device_info *mac = priv->hw;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index 8a6a6f79bde6..ddceb33dcd40 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -102,6 +102,29 @@ static void dwmac1000_dma_init_tx(struct stmmac_priv *priv,
writel(lower_32_bits(dma_tx_phy), ioaddr + DMA_CHAN_TX_BASE_ADDR(chan));
}
+static void dwmac1000_dma_clean(struct stmmac_priv *priv, void __iomem *ioaddr)
+{
+ const unsigned int maxch = 8;
+ int ch;
+
+ for (ch = 0; ch < maxch; ch++) {
+ /* Restore default CSRs state */
+ writel(0x00020100, ioaddr + DMA_CHAN_BUS_MODE(ch));
+ writel(0, ioaddr + DMA_CHAN_RCV_BASE_ADDR(ch));
+ writel(0, ioaddr + DMA_CHAN_TX_BASE_ADDR(ch));
+ writel(0, ioaddr + DMA_CHAN_CONTROL(ch));
+ writel(0, ioaddr + DMA_CHAN_INTR_ENA(ch));
+ writel(0, ioaddr + DMA_CHAN_RX_WATCHDOG(ch));
+
+ /* Clear the pending statuses */
+ readl(ioaddr + DMA_CHAN_MISSED_FRAME_CTR(ch));
+ writel(0x0007FFFF, ioaddr + DMA_CHAN_STATUS(ch));
+ }
+
+ /* AXI Bus Mode CSR is shared for all channel */
+ writel(0x00110001, ioaddr + DMA_AXI_BUS_MODE);
+}
+
static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
{
csr6 &= ~DMA_CONTROL_RFA_MASK;
@@ -286,6 +309,7 @@ const struct stmmac_dma_ops dwmac1000_dma_ops = {
.init_chan = dwmac1000_dma_init_channel,
.init_rx_chan = dwmac1000_dma_init_rx,
.init_tx_chan = dwmac1000_dma_init_tx,
+ .clean = dwmac1000_dma_clean,
.axi = dwmac1000_dma_axi,
.dump_regs = dwmac1000_dump_dma_regs,
.dma_rx_mode = dwmac1000_dma_operation_mode_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.c b/drivers/net/ethernet/stmicro/stmmac/hwif.c
index ddf1f201969b..6ed6be48323c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.c
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.c
@@ -161,6 +161,7 @@ static const struct stmmac_hwif_entry {
.ptp = &dwmac1000_ptp_clock_ops,
.tc = NULL,
.mmc = &dwmac_mmc_ops,
+ .gpio = &dwmac1000_gpio_ops,
.setup = dwmac1000_setup,
.quirks = stmmac_dwmac1_quirks,
}, {
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index c8b64b6d47ea..c19782bf5e20 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -708,6 +708,7 @@ extern const struct stmmac_dma_ops dwmac100_dma_ops;
extern const struct stmmac_ops dwmac1000_ops;
extern const struct stmmac_dma_ops dwmac1000_dma_ops;
extern const struct stmmac_vlan_ops dwmac1000_vlan_ops;
+extern const struct stmmac_gpio_ops dwmac1000_gpio_ops;
extern const struct stmmac_ops dwmac4_ops;
extern const struct stmmac_dma_ops dwmac4_dma_ops;
extern const struct stmmac_ops dwmac410_ops;