diff options
| author | Serge Semin <fancer.lancer@gmail.com> | 2025-09-12 14:08:44 +0300 |
|---|---|---|
| committer | Serge Semin <fancer.lancer@gmail.com> | 2026-08-13 20:36:54 +0300 |
| commit | 995f7a1b5ff5b86db25d2cd484cf565bce7b1e65 (patch) | |
| tree | ef49f5652024124afdfd8862220d03f272c53931 | |
| parent | a8c19a421893fca887060cd525f3e86371fd1801 (diff) | |
| download | linux-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.h | 14 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 162 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 24 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/hwif.c | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/hwif.h | 1 |
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; |
