175a6faf6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
248863ce5SAlexandre TORGUE /*
348863ce5SAlexandre TORGUE * Copyright (C) 2007-2015 STMicroelectronics Ltd
448863ce5SAlexandre TORGUE *
548863ce5SAlexandre TORGUE * Author: Alexandre Torgue <alexandre.torgue@st.com>
648863ce5SAlexandre TORGUE */
748863ce5SAlexandre TORGUE
848863ce5SAlexandre TORGUE #include <linux/io.h>
945d0da49SDejin Zheng #include <linux/iopoll.h>
1048863ce5SAlexandre TORGUE #include <linux/delay.h>
1148863ce5SAlexandre TORGUE #include "common.h"
1248863ce5SAlexandre TORGUE #include "dwmac4_dma.h"
1348863ce5SAlexandre TORGUE #include "dwmac4.h"
1433719b57SAndrew Halaney #include "stmmac.h"
1548863ce5SAlexandre TORGUE
dwmac4_dma_reset(void __iomem * ioaddr)1648863ce5SAlexandre TORGUE int dwmac4_dma_reset(void __iomem *ioaddr)
1748863ce5SAlexandre TORGUE {
1848863ce5SAlexandre TORGUE u32 value = readl(ioaddr + DMA_BUS_MODE);
1948863ce5SAlexandre TORGUE
2048863ce5SAlexandre TORGUE /* DMA SW reset */
2148863ce5SAlexandre TORGUE value |= DMA_BUS_MODE_SFT_RESET;
2248863ce5SAlexandre TORGUE writel(value, ioaddr + DMA_BUS_MODE);
2348863ce5SAlexandre TORGUE
2445d0da49SDejin Zheng return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
2545d0da49SDejin Zheng !(value & DMA_BUS_MODE_SFT_RESET),
269d14edfdSFugang Duan 10000, 1000000);
2748863ce5SAlexandre TORGUE }
2848863ce5SAlexandre TORGUE
dwmac4_set_rx_tail_ptr(struct stmmac_priv * priv,void __iomem * ioaddr,u32 tail_ptr,u32 chan)291d84b487SAndrew Halaney void dwmac4_set_rx_tail_ptr(struct stmmac_priv *priv, void __iomem *ioaddr,
301d84b487SAndrew Halaney u32 tail_ptr, u32 chan)
3148863ce5SAlexandre TORGUE {
3233719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
3333719b57SAndrew Halaney
3433719b57SAndrew Halaney writel(tail_ptr, ioaddr + DMA_CHAN_RX_END_ADDR(dwmac4_addrs, chan));
3548863ce5SAlexandre TORGUE }
3648863ce5SAlexandre TORGUE
dwmac4_set_tx_tail_ptr(struct stmmac_priv * priv,void __iomem * ioaddr,u32 tail_ptr,u32 chan)371d84b487SAndrew Halaney void dwmac4_set_tx_tail_ptr(struct stmmac_priv *priv, void __iomem *ioaddr,
381d84b487SAndrew Halaney u32 tail_ptr, u32 chan)
3948863ce5SAlexandre TORGUE {
4033719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
4133719b57SAndrew Halaney
4233719b57SAndrew Halaney writel(tail_ptr, ioaddr + DMA_CHAN_TX_END_ADDR(dwmac4_addrs, chan));
4348863ce5SAlexandre TORGUE }
4448863ce5SAlexandre TORGUE
dwmac4_dma_start_tx(struct stmmac_priv * priv,void __iomem * ioaddr,u32 chan)451d84b487SAndrew Halaney void dwmac4_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
461d84b487SAndrew Halaney u32 chan)
4748863ce5SAlexandre TORGUE {
4833719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
4933719b57SAndrew Halaney u32 value = readl(ioaddr + DMA_CHAN_TX_CONTROL(dwmac4_addrs, chan));
5048863ce5SAlexandre TORGUE
5148863ce5SAlexandre TORGUE value |= DMA_CONTROL_ST;
5233719b57SAndrew Halaney writel(value, ioaddr + DMA_CHAN_TX_CONTROL(dwmac4_addrs, chan));
5348863ce5SAlexandre TORGUE
5448863ce5SAlexandre TORGUE value = readl(ioaddr + GMAC_CONFIG);
5548863ce5SAlexandre TORGUE value |= GMAC_CONFIG_TE;
5648863ce5SAlexandre TORGUE writel(value, ioaddr + GMAC_CONFIG);
5748863ce5SAlexandre TORGUE }
5848863ce5SAlexandre TORGUE
dwmac4_dma_stop_tx(struct stmmac_priv * priv,void __iomem * ioaddr,u32 chan)591d84b487SAndrew Halaney void dwmac4_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
601d84b487SAndrew Halaney u32 chan)
6148863ce5SAlexandre TORGUE {
6233719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
6333719b57SAndrew Halaney
6433719b57SAndrew Halaney u32 value = readl(ioaddr + DMA_CHAN_TX_CONTROL(dwmac4_addrs, chan));
6548863ce5SAlexandre TORGUE
6648863ce5SAlexandre TORGUE value &= ~DMA_CONTROL_ST;
6733719b57SAndrew Halaney writel(value, ioaddr + DMA_CHAN_TX_CONTROL(dwmac4_addrs, chan));
6848863ce5SAlexandre TORGUE }
6948863ce5SAlexandre TORGUE
dwmac4_dma_start_rx(struct stmmac_priv * priv,void __iomem * ioaddr,u32 chan)701d84b487SAndrew Halaney void dwmac4_dma_start_rx(struct stmmac_priv *priv, void __iomem *ioaddr,
711d84b487SAndrew Halaney u32 chan)
7248863ce5SAlexandre TORGUE {
7333719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
7433719b57SAndrew Halaney
7533719b57SAndrew Halaney u32 value = readl(ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan));
7648863ce5SAlexandre TORGUE
7748863ce5SAlexandre TORGUE value |= DMA_CONTROL_SR;
7848863ce5SAlexandre TORGUE
7933719b57SAndrew Halaney writel(value, ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan));
8048863ce5SAlexandre TORGUE
8148863ce5SAlexandre TORGUE value = readl(ioaddr + GMAC_CONFIG);
8248863ce5SAlexandre TORGUE value |= GMAC_CONFIG_RE;
8348863ce5SAlexandre TORGUE writel(value, ioaddr + GMAC_CONFIG);
8448863ce5SAlexandre TORGUE }
8548863ce5SAlexandre TORGUE
dwmac4_dma_stop_rx(struct stmmac_priv * priv,void __iomem * ioaddr,u32 chan)861d84b487SAndrew Halaney void dwmac4_dma_stop_rx(struct stmmac_priv *priv, void __iomem *ioaddr,
871d84b487SAndrew Halaney u32 chan)
8848863ce5SAlexandre TORGUE {
8933719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
9033719b57SAndrew Halaney u32 value = readl(ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan));
9148863ce5SAlexandre TORGUE
9248863ce5SAlexandre TORGUE value &= ~DMA_CONTROL_SR;
9333719b57SAndrew Halaney writel(value, ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan));
9448863ce5SAlexandre TORGUE }
9548863ce5SAlexandre TORGUE
dwmac4_set_tx_ring_len(struct stmmac_priv * priv,void __iomem * ioaddr,u32 len,u32 chan)961d84b487SAndrew Halaney void dwmac4_set_tx_ring_len(struct stmmac_priv *priv, void __iomem *ioaddr,
971d84b487SAndrew Halaney u32 len, u32 chan)
9848863ce5SAlexandre TORGUE {
9933719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
10033719b57SAndrew Halaney
10133719b57SAndrew Halaney writel(len, ioaddr + DMA_CHAN_TX_RING_LEN(dwmac4_addrs, chan));
10248863ce5SAlexandre TORGUE }
10348863ce5SAlexandre TORGUE
dwmac4_set_rx_ring_len(struct stmmac_priv * priv,void __iomem * ioaddr,u32 len,u32 chan)1041d84b487SAndrew Halaney void dwmac4_set_rx_ring_len(struct stmmac_priv *priv, void __iomem *ioaddr,
1051d84b487SAndrew Halaney u32 len, u32 chan)
10648863ce5SAlexandre TORGUE {
10733719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
10833719b57SAndrew Halaney
10933719b57SAndrew Halaney writel(len, ioaddr + DMA_CHAN_RX_RING_LEN(dwmac4_addrs, chan));
11048863ce5SAlexandre TORGUE }
11148863ce5SAlexandre TORGUE
dwmac4_enable_dma_irq(struct stmmac_priv * priv,void __iomem * ioaddr,u32 chan,bool rx,bool tx)1121d84b487SAndrew Halaney void dwmac4_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
1131d84b487SAndrew Halaney u32 chan, bool rx, bool tx)
11448863ce5SAlexandre TORGUE {
11533719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
11633719b57SAndrew Halaney u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
117021bd5e3SJose Abreu
118021bd5e3SJose Abreu if (rx)
119021bd5e3SJose Abreu value |= DMA_CHAN_INTR_DEFAULT_RX;
120021bd5e3SJose Abreu if (tx)
121021bd5e3SJose Abreu value |= DMA_CHAN_INTR_DEFAULT_TX;
122021bd5e3SJose Abreu
12333719b57SAndrew Halaney writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
12448863ce5SAlexandre TORGUE }
12548863ce5SAlexandre TORGUE
dwmac410_enable_dma_irq(struct stmmac_priv * priv,void __iomem * ioaddr,u32 chan,bool rx,bool tx)1261d84b487SAndrew Halaney void dwmac410_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
1271d84b487SAndrew Halaney u32 chan, bool rx, bool tx)
12848863ce5SAlexandre TORGUE {
12933719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
13033719b57SAndrew Halaney u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
131021bd5e3SJose Abreu
132021bd5e3SJose Abreu if (rx)
133021bd5e3SJose Abreu value |= DMA_CHAN_INTR_DEFAULT_RX_4_10;
134021bd5e3SJose Abreu if (tx)
135021bd5e3SJose Abreu value |= DMA_CHAN_INTR_DEFAULT_TX_4_10;
136021bd5e3SJose Abreu
13733719b57SAndrew Halaney writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
13848863ce5SAlexandre TORGUE }
13948863ce5SAlexandre TORGUE
dwmac4_disable_dma_irq(struct stmmac_priv * priv,void __iomem * ioaddr,u32 chan,bool rx,bool tx)1401d84b487SAndrew Halaney void dwmac4_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
1411d84b487SAndrew Halaney u32 chan, bool rx, bool tx)
14248863ce5SAlexandre TORGUE {
14333719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
14433719b57SAndrew Halaney u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
145021bd5e3SJose Abreu
146021bd5e3SJose Abreu if (rx)
147021bd5e3SJose Abreu value &= ~DMA_CHAN_INTR_DEFAULT_RX;
148021bd5e3SJose Abreu if (tx)
149021bd5e3SJose Abreu value &= ~DMA_CHAN_INTR_DEFAULT_TX;
150021bd5e3SJose Abreu
15133719b57SAndrew Halaney writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
152021bd5e3SJose Abreu }
153021bd5e3SJose Abreu
dwmac410_disable_dma_irq(struct stmmac_priv * priv,void __iomem * ioaddr,u32 chan,bool rx,bool tx)1541d84b487SAndrew Halaney void dwmac410_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
1551d84b487SAndrew Halaney u32 chan, bool rx, bool tx)
156021bd5e3SJose Abreu {
15733719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
15833719b57SAndrew Halaney u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
159021bd5e3SJose Abreu
160021bd5e3SJose Abreu if (rx)
161021bd5e3SJose Abreu value &= ~DMA_CHAN_INTR_DEFAULT_RX_4_10;
162021bd5e3SJose Abreu if (tx)
163021bd5e3SJose Abreu value &= ~DMA_CHAN_INTR_DEFAULT_TX_4_10;
164021bd5e3SJose Abreu
16533719b57SAndrew Halaney writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
16648863ce5SAlexandre TORGUE }
16748863ce5SAlexandre TORGUE
dwmac4_dma_interrupt(struct stmmac_priv * priv,void __iomem * ioaddr,struct stmmac_extra_stats * x,u32 chan,u32 dir)1681d84b487SAndrew Halaney int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
1697e1c520cSOng Boon Leong struct stmmac_extra_stats *x, u32 chan, u32 dir)
17048863ce5SAlexandre TORGUE {
17133719b57SAndrew Halaney const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
17233719b57SAndrew Halaney u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan));
17333719b57SAndrew Halaney u32 intr_en = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
174*9680b2abSPetr Tesarik struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats);
1751103d3a5SJose Abreu int ret = 0;
17648863ce5SAlexandre TORGUE
1777e1c520cSOng Boon Leong if (dir == DMA_DIR_RX)
1787e1c520cSOng Boon Leong intr_status &= DMA_CHAN_STATUS_MSK_RX;
1797e1c520cSOng Boon Leong else if (dir == DMA_DIR_TX)
1807e1c520cSOng Boon Leong intr_status &= DMA_CHAN_STATUS_MSK_TX;
1817e1c520cSOng Boon Leong
18248863ce5SAlexandre TORGUE /* ABNORMAL interrupts */
18348863ce5SAlexandre TORGUE if (unlikely(intr_status & DMA_CHAN_STATUS_AIS)) {
18448863ce5SAlexandre TORGUE if (unlikely(intr_status & DMA_CHAN_STATUS_RBU))
18548863ce5SAlexandre TORGUE x->rx_buf_unav_irq++;
18648863ce5SAlexandre TORGUE if (unlikely(intr_status & DMA_CHAN_STATUS_RPS))
18748863ce5SAlexandre TORGUE x->rx_process_stopped_irq++;
18848863ce5SAlexandre TORGUE if (unlikely(intr_status & DMA_CHAN_STATUS_RWT))
18948863ce5SAlexandre TORGUE x->rx_watchdog_irq++;
19048863ce5SAlexandre TORGUE if (unlikely(intr_status & DMA_CHAN_STATUS_ETI))
19148863ce5SAlexandre TORGUE x->tx_early_irq++;
19248863ce5SAlexandre TORGUE if (unlikely(intr_status & DMA_CHAN_STATUS_TPS)) {
19348863ce5SAlexandre TORGUE x->tx_process_stopped_irq++;
19448863ce5SAlexandre TORGUE ret = tx_hard_error;
19548863ce5SAlexandre TORGUE }
19648863ce5SAlexandre TORGUE if (unlikely(intr_status & DMA_CHAN_STATUS_FBE)) {
19748863ce5SAlexandre TORGUE x->fatal_bus_error_irq++;
19848863ce5SAlexandre TORGUE ret = tx_hard_error;
19948863ce5SAlexandre TORGUE }
20048863ce5SAlexandre TORGUE }
20148863ce5SAlexandre TORGUE /* TX/RX NORMAL interrupts */
20248863ce5SAlexandre TORGUE if (likely(intr_status & DMA_CHAN_STATUS_RI)) {
203*9680b2abSPetr Tesarik u64_stats_update_begin(&stats->syncp);
204*9680b2abSPetr Tesarik u64_stats_inc(&stats->rx_normal_irq_n[chan]);
205*9680b2abSPetr Tesarik u64_stats_update_end(&stats->syncp);
20648863ce5SAlexandre TORGUE ret |= handle_rx;
20748863ce5SAlexandre TORGUE }
2081975df88SVoon Weifeng if (likely(intr_status & DMA_CHAN_STATUS_TI)) {
209*9680b2abSPetr Tesarik u64_stats_update_begin(&stats->syncp);
210*9680b2abSPetr Tesarik u64_stats_inc(&stats->tx_normal_irq_n[chan]);
211*9680b2abSPetr Tesarik u64_stats_update_end(&stats->syncp);
21248863ce5SAlexandre TORGUE ret |= handle_tx;
21348863ce5SAlexandre TORGUE }
214133466c3SJisheng Zhang
2151975df88SVoon Weifeng if (unlikely(intr_status & DMA_CHAN_STATUS_TBU))
2161975df88SVoon Weifeng ret |= handle_tx;
21748863ce5SAlexandre TORGUE if (unlikely(intr_status & DMA_CHAN_STATUS_ERI))
21848863ce5SAlexandre TORGUE x->rx_early_irq++;
21948863ce5SAlexandre TORGUE
22033719b57SAndrew Halaney writel(intr_status & intr_en,
22133719b57SAndrew Halaney ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan));
22248863ce5SAlexandre TORGUE return ret;
22348863ce5SAlexandre TORGUE }
22448863ce5SAlexandre TORGUE
stmmac_dwmac4_set_mac_addr(void __iomem * ioaddr,const u8 addr[6],unsigned int high,unsigned int low)22576660757SJakub Kicinski void stmmac_dwmac4_set_mac_addr(void __iomem *ioaddr, const u8 addr[6],
22648863ce5SAlexandre TORGUE unsigned int high, unsigned int low)
22748863ce5SAlexandre TORGUE {
22848863ce5SAlexandre TORGUE unsigned long data;
22948863ce5SAlexandre TORGUE
23048863ce5SAlexandre TORGUE data = (addr[5] << 8) | addr[4];
23148863ce5SAlexandre TORGUE /* For MAC Addr registers se have to set the Address Enable (AE)
23248863ce5SAlexandre TORGUE * bit that has no effect on the High Reg 0 where the bit 31 (MO)
23348863ce5SAlexandre TORGUE * is RO.
23448863ce5SAlexandre TORGUE */
23548863ce5SAlexandre TORGUE data |= (STMMAC_CHAN0 << GMAC_HI_DCS_SHIFT);
23648863ce5SAlexandre TORGUE writel(data | GMAC_HI_REG_AE, ioaddr + high);
23748863ce5SAlexandre TORGUE data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
23848863ce5SAlexandre TORGUE writel(data, ioaddr + low);
23948863ce5SAlexandre TORGUE }
24048863ce5SAlexandre TORGUE
24148863ce5SAlexandre TORGUE /* Enable disable MAC RX/TX */
stmmac_dwmac4_set_mac(void __iomem * ioaddr,bool enable)24248863ce5SAlexandre TORGUE void stmmac_dwmac4_set_mac(void __iomem *ioaddr, bool enable)
24348863ce5SAlexandre TORGUE {
24448863ce5SAlexandre TORGUE u32 value = readl(ioaddr + GMAC_CONFIG);
245284779dbSVincent Whitchurch u32 old_val = value;
24648863ce5SAlexandre TORGUE
24748863ce5SAlexandre TORGUE if (enable)
24848863ce5SAlexandre TORGUE value |= GMAC_CONFIG_RE | GMAC_CONFIG_TE;
24948863ce5SAlexandre TORGUE else
25048863ce5SAlexandre TORGUE value &= ~(GMAC_CONFIG_TE | GMAC_CONFIG_RE);
25148863ce5SAlexandre TORGUE
252284779dbSVincent Whitchurch if (value != old_val)
25348863ce5SAlexandre TORGUE writel(value, ioaddr + GMAC_CONFIG);
25448863ce5SAlexandre TORGUE }
25548863ce5SAlexandre TORGUE
stmmac_dwmac4_get_mac_addr(void __iomem * ioaddr,unsigned char * addr,unsigned int high,unsigned int low)25648863ce5SAlexandre TORGUE void stmmac_dwmac4_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
25748863ce5SAlexandre TORGUE unsigned int high, unsigned int low)
25848863ce5SAlexandre TORGUE {
25948863ce5SAlexandre TORGUE unsigned int hi_addr, lo_addr;
26048863ce5SAlexandre TORGUE
26148863ce5SAlexandre TORGUE /* Read the MAC address from the hardware */
26248863ce5SAlexandre TORGUE hi_addr = readl(ioaddr + high);
26348863ce5SAlexandre TORGUE lo_addr = readl(ioaddr + low);
26448863ce5SAlexandre TORGUE
26548863ce5SAlexandre TORGUE /* Extract the MAC address from the high and low words */
26648863ce5SAlexandre TORGUE addr[0] = lo_addr & 0xff;
26748863ce5SAlexandre TORGUE addr[1] = (lo_addr >> 8) & 0xff;
26848863ce5SAlexandre TORGUE addr[2] = (lo_addr >> 16) & 0xff;
26948863ce5SAlexandre TORGUE addr[3] = (lo_addr >> 24) & 0xff;
27048863ce5SAlexandre TORGUE addr[4] = hi_addr & 0xff;
27148863ce5SAlexandre TORGUE addr[5] = (hi_addr >> 8) & 0xff;
27248863ce5SAlexandre TORGUE }
273