@@ -448,6 +448,8 @@ struct mac_device_info {
unsigned int pcs;
unsigned int pmt;
unsigned int ps;
+ bool mdio_intr_en;
+ wait_queue_head_t mdio_busy_wait;
};
struct stmmac_rx_routing {
@@ -106,6 +106,7 @@ enum dwmac4_irq_status {
mmc_irq = 0x00000100,
lpi_irq = 0x00000020,
pmt_irq = 0x00000010,
+ mdio_irq = 0x00040000,
};
/* MAC PMT bitmap */
@@ -59,6 +59,9 @@ static void dwmac4_core_init(struct mac_device_info *hw,
if (hw->pcs)
value |= GMAC_PCS_IRQ_DEFAULT;
+ if (hw->mdio_intr_en)
+ value |= GMAC_INT_MDIO_EN;
+
writel(value, ioaddr + GMAC_INT_EN);
}
@@ -629,6 +632,9 @@ static int dwmac4_irq_status(struct mac_device_info *hw,
x->irq_rx_path_exit_lpi_mode_n++;
}
+ if (intr_status & mdio_irq)
+ wake_up(&hw->mdio_busy_wait);
+
dwmac_pcs_isr(ioaddr, GMAC_PCS_BASE, intr_status, x);
if (intr_status & PCS_RGSMIIIS_IRQ)
dwmac4_phystatus(ioaddr, x);
@@ -836,6 +842,7 @@ static void dwmac4_set_mac_loopback(void __iomem *ioaddr, bool enable)
.rxp_config = dwmac5_rxp_config,
.flex_pps_config = dwmac5_flex_pps_config,
.set_mac_loopback = dwmac4_set_mac_loopback,
+ .mdio_intr_dis = dwmac5_mdio_intr_dis,
};
int dwmac4_setup(struct stmmac_priv *priv)
@@ -549,3 +549,11 @@ int dwmac5_flex_pps_config(void __iomem *ioaddr, int index,
writel(val, ioaddr + MAC_PPS_CONTROL);
return 0;
}
+
+void dwmac5_mdio_intr_dis(void __iomem *ioaddr)
+{
+ u32 val = readl(ioaddr + GMAC_INT_EN);
+
+ val &= ~GMAC_INT_MDIO_EN;
+ writel(val, ioaddr + GMAC_INT_EN);
+}
@@ -72,6 +72,9 @@
#define TCEIE BIT(0)
#define DMA_ECC_INT_STATUS 0x00001088
+/* MDIO interrupt enable in MAC_Interrupt_Enable register */
+#define GMAC_INT_MDIO_EN BIT(18)
+
int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp);
int dwmac5_safety_feat_irq_status(struct net_device *ndev,
void __iomem *ioaddr, unsigned int asp,
@@ -83,5 +86,6 @@ int dwmac5_rxp_config(void __iomem *ioaddr, struct stmmac_tc_entry *entries,
int dwmac5_flex_pps_config(void __iomem *ioaddr, int index,
struct stmmac_pps_cfg *cfg, bool enable,
u32 sub_second_inc, u32 systime_flags);
+void dwmac5_mdio_intr_dis(void __iomem *ioaddr);
#endif /* __DWMAC5_H__ */
@@ -73,6 +73,7 @@ static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
bool gmac;
bool gmac4;
bool xgmac;
+ bool mdio_intr_en;
u32 min_id;
const struct stmmac_regs_off regs;
const void *desc;
@@ -90,6 +91,7 @@ static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
.gmac = false,
.gmac4 = false,
.xgmac = false,
+ .mdio_intr_en = false,
.min_id = 0,
.regs = {
.ptp_off = PTP_GMAC3_X_OFFSET,
@@ -108,6 +110,7 @@ static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
.gmac = true,
.gmac4 = false,
.xgmac = false,
+ .mdio_intr_en = false,
.min_id = 0,
.regs = {
.ptp_off = PTP_GMAC3_X_OFFSET,
@@ -126,6 +129,7 @@ static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
.gmac = false,
.gmac4 = true,
.xgmac = false,
+ .mdio_intr_en = false,
.min_id = 0,
.regs = {
.ptp_off = PTP_GMAC4_OFFSET,
@@ -144,6 +148,7 @@ static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
.gmac = false,
.gmac4 = true,
.xgmac = false,
+ .mdio_intr_en = false,
.min_id = DWMAC_CORE_4_00,
.regs = {
.ptp_off = PTP_GMAC4_OFFSET,
@@ -162,6 +167,7 @@ static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
.gmac = false,
.gmac4 = true,
.xgmac = false,
+ .mdio_intr_en = false,
.min_id = DWMAC_CORE_4_10,
.regs = {
.ptp_off = PTP_GMAC4_OFFSET,
@@ -180,6 +186,7 @@ static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
.gmac = false,
.gmac4 = true,
.xgmac = false,
+ .mdio_intr_en = true,
.min_id = DWMAC_CORE_5_10,
.regs = {
.ptp_off = PTP_GMAC4_OFFSET,
@@ -198,6 +205,7 @@ static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
.gmac = false,
.gmac4 = false,
.xgmac = true,
+ .mdio_intr_en = false,
.min_id = DWXGMAC_CORE_2_10,
.regs = {
.ptp_off = PTP_XGMAC_OFFSET,
@@ -276,6 +284,7 @@ int stmmac_hwif_init(struct stmmac_priv *priv)
mac->mode = mac->mode ? : entry->mode;
mac->tc = mac->tc ? : entry->tc;
mac->mmc = mac->mmc ? : entry->mmc;
+ mac->mdio_intr_en = mac->mdio_intr_en ? : entry->mdio_intr_en;
priv->hw = mac;
priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off;
@@ -363,6 +363,8 @@ struct stmmac_ops {
int (*get_mac_tx_timestamp)(struct mac_device_info *hw, u64 *ts);
/* Source Address Insertion / Replacement */
void (*sarc_configure)(void __iomem *ioaddr, int val);
+ /* Disable mdio interrupt */
+ void (*mdio_intr_dis)(void __iomem *ioaddr);
};
#define stmmac_core_init(__priv, __args...) \
@@ -443,6 +445,8 @@ struct stmmac_ops {
stmmac_do_callback(__priv, mac, get_mac_tx_timestamp, __args)
#define stmmac_sarc_configure(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, sarc_configure, __args)
+#define stmmac_mdio_intr_dis(__priv, __args...) \
+ stmmac_do_void_callback(__priv, mac, mdio_intr_dis, __args)
/* PTP and HW Timer helpers */
struct stmmac_hwtimestamp {
@@ -2768,6 +2768,8 @@ static int stmmac_release(struct net_device *dev)
phylink_stop(priv->phylink);
phylink_disconnect_phy(priv->phylink);
+ stmmac_mdio_intr_dis(priv, priv->ioaddr);
+
stmmac_stop_all_queues(priv);
stmmac_disable_all_queues(priv);
@@ -4463,6 +4465,9 @@ int stmmac_dvr_probe(struct device *device,
if (ret)
goto error_hw_init;
+ /* mdio intr wait queue */
+ init_waitqueue_head(&priv->hw->mdio_busy_wait);
+
stmmac_check_ether_addr(priv);
/* Configure real RX and TX queues */
@@ -19,6 +19,8 @@
#include <linux/property.h>
#include <linux/slab.h>
+#include "dwmac4.h"
+#include "dwmac5.h"
#include "dwxgmac2.h"
#include "stmmac.h"
@@ -142,6 +144,18 @@ static int stmmac_xgmac2_mdio_write(struct mii_bus *bus, int phyaddr,
!(tmp & MII_XGMAC_BUSY), 100, 10000);
}
+static bool stmmac_mdio_intr_done(struct mii_bus *bus)
+{
+ struct net_device *ndev = bus->priv;
+ struct stmmac_priv *priv;
+ unsigned int mii_address;
+
+ priv = netdev_priv(ndev);
+ mii_address = priv->hw->mii.addr;
+
+ return !(readl(priv->ioaddr + mii_address) & MII_BUSY);
+}
+
/**
* stmmac_mdio_read
* @bus: points to the mii_bus structure
@@ -159,9 +173,11 @@ static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
unsigned int mii_address = priv->hw->mii.addr;
unsigned int mii_data = priv->hw->mii.data;
u32 value = MII_BUSY;
+ u32 mdio_intr_en = 0;
int data = 0;
u32 v;
+ mdio_intr_en = readl(priv->ioaddr + GMAC_INT_EN) & GMAC_INT_MDIO_EN;
value |= (phyaddr << priv->hw->mii.addr_shift)
& priv->hw->mii.addr_mask;
value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
@@ -181,16 +197,26 @@ static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
}
}
- if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
- 100, 10000))
+ if (mdio_intr_en) {
+ if (!wait_event_timeout(priv->hw->mdio_busy_wait,
+ stmmac_mdio_intr_done(bus), HZ / 100))
+ return -EBUSY;
+ } else if (readl_poll_timeout(priv->ioaddr + mii_address, v,
+ !(v & MII_BUSY), 100, 10000)) {
return -EBUSY;
+ }
writel(data, priv->ioaddr + mii_data);
writel(value, priv->ioaddr + mii_address);
- if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
- 100, 10000))
+ if (mdio_intr_en) {
+ if (!wait_event_timeout(priv->hw->mdio_busy_wait,
+ stmmac_mdio_intr_done(bus), HZ / 100))
+ return -EBUSY;
+ } else if (readl_poll_timeout(priv->ioaddr + mii_address, v,
+ !(v & MII_BUSY), 100, 10000)) {
return -EBUSY;
+ }
/* Read the data from the MII data register */
data = (int)readl(priv->ioaddr + mii_data) & MII_DATA_MASK;
@@ -214,9 +240,11 @@ static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg,
unsigned int mii_address = priv->hw->mii.addr;
unsigned int mii_data = priv->hw->mii.data;
u32 value = MII_BUSY;
+ u32 mdio_intr_en = 0;
int data = phydata;
u32 v;
+ mdio_intr_en = readl(priv->ioaddr + GMAC_INT_EN) & GMAC_INT_MDIO_EN;
value |= (phyaddr << priv->hw->mii.addr_shift)
& priv->hw->mii.addr_mask;
value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
@@ -240,17 +268,30 @@ static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg,
}
/* Wait until any existing MII operation is complete */
- if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
- 100, 10000))
+ if (mdio_intr_en) {
+ if (!wait_event_timeout(priv->hw->mdio_busy_wait,
+ stmmac_mdio_intr_done(bus), HZ / 100))
+ return -EBUSY;
+ } else if (readl_poll_timeout(priv->ioaddr + mii_address, v,
+ !(v & MII_BUSY), 100, 10000)) {
return -EBUSY;
+ }
/* Set the MII address register to write */
writel(data, priv->ioaddr + mii_data);
writel(value, priv->ioaddr + mii_address);
/* Wait until any existing MII operation is complete */
- return readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
- 100, 10000);
+ if (mdio_intr_en) {
+ if (!wait_event_timeout(priv->hw->mdio_busy_wait,
+ stmmac_mdio_intr_done(bus), HZ / 100))
+ return -EBUSY;
+ } else if (readl_poll_timeout(priv->ioaddr + mii_address, v,
+ !(v & MII_BUSY), 100, 10000)) {
+ return -EBUSY;
+ }
+
+ return 0;
}
/**