@@ -407,6 +407,9 @@ struct mii_regs {
unsigned int clk_csr_mask;
};
+/* tsn capability, meant for mac_device_info->tsn_cap */
+#define TSN_CAP_EST BIT(0)
+
struct mac_device_info {
const struct stmmac_ops *mac;
const struct stmmac_desc_ops *desc;
@@ -425,6 +428,7 @@ struct mac_device_info {
unsigned int pcs;
unsigned int pmt;
unsigned int ps;
+ u32 tsn_cap;
};
struct stmmac_rx_routing {
@@ -11,6 +11,7 @@
static bool dw_tsn_feat_en[TSN_FEAT_ID_MAX];
static unsigned int dw_tsn_hwtunable[TSN_HWTUNA_MAX];
static struct est_gc_config dw_est_gc_config;
+static struct tsn_err_stat dw_err_stat;
static unsigned int est_get_gcl_depth(unsigned int hw_cap)
{
@@ -295,6 +296,24 @@ void dwmac_tsn_init(void *ioaddr)
gcl_depth, ti_wid, tils_max, cap->txqcnt);
}
+/* dwmac_tsn_setup is called within stmmac_hw_setup() after
+ * stmmac_init_dma_engine() which resets MAC controller.
+ * This is so-that MAC registers are not cleared.
+ */
+void dwmac_tsn_setup(void *ioaddr)
+{
+ struct tsn_hw_cap *cap = &dw_tsn_hwcap;
+ unsigned int value;
+
+ if (cap->est_support) {
+ /* Enable EST interrupts */
+ value = (MTL_EST_INT_EN_CGCE | MTL_EST_INT_EN_IEHS |
+ MTL_EST_INT_EN_IEHF | MTL_EST_INT_EN_IEBE |
+ MTL_EST_INT_EN_IECC);
+ TSN_WR32(value, ioaddr + MTL_EST_INT_EN);
+ }
+}
+
void dwmac_get_tsn_hwcap(struct tsn_hw_cap **tsn_hwcap)
{
*tsn_hwcap = &dw_tsn_hwcap;
@@ -788,3 +807,107 @@ int dwmac_get_est_gcc(void *ioaddr,
return 0;
}
+
+int dwmac_est_irq_status(void *ioaddr)
+{
+ struct tsn_err_stat *err_stat = &dw_err_stat;
+ struct tsn_hw_cap *cap = &dw_tsn_hwcap;
+ unsigned int txqcnt_mask = 0;
+ unsigned int status = 0;
+ unsigned int value = 0;
+ unsigned int feqn = 0;
+ unsigned int hbfq = 0;
+ unsigned int hbfs = 0;
+
+ txqcnt_mask = (1 << cap->txqcnt) - 1;
+ status = TSN_RD32(ioaddr + MTL_EST_STATUS);
+
+ value = (MTL_EST_STATUS_CGCE | MTL_EST_STATUS_HLBS |
+ MTL_EST_STATUS_HLBF | MTL_EST_STATUS_BTRE |
+ MTL_EST_STATUS_SWLC);
+
+ /* Return if there is no error */
+ if (!(status & value))
+ return 0;
+
+ /* spin_lock() is not needed here because of BTRE and SWLC
+ * bit will not be altered. Both of the bit will be
+ * polled in dwmac_set_est_gcrr_times()
+ */
+ if (status & MTL_EST_STATUS_CGCE) {
+ /* Clear Interrupt */
+ TSN_WR32(MTL_EST_STATUS_CGCE, ioaddr + MTL_EST_STATUS);
+
+ err_stat->cgce_n++;
+ }
+
+ if (status & MTL_EST_STATUS_HLBS) {
+ value = TSN_RD32(ioaddr + MTL_EST_SCH_ERR);
+ value &= txqcnt_mask;
+
+ /* Clear Interrupt */
+ TSN_WR32(value, ioaddr + MTL_EST_SCH_ERR);
+
+ /* Collecting info to shows all the queues that has HLBS */
+ /* issue. The only way to clear this is to clear the */
+ /* statistic */
+ err_stat->hlbs_q |= value;
+ }
+
+ if (status & MTL_EST_STATUS_HLBF) {
+ value = TSN_RD32(ioaddr + MTL_EST_FRM_SZ_ERR);
+ feqn = value & txqcnt_mask;
+
+ value = TSN_RD32(ioaddr + MTL_EST_FRM_SZ_CAP);
+ hbfq = (value & MTL_EST_FRM_SZ_CAP_HBFQ_MASK(cap->txqcnt))
+ >> MTL_EST_FRM_SZ_CAP_HBFQ_SHIFT;
+ hbfs = value & MTL_EST_FRM_SZ_CAP_HBFS_MASK;
+
+ /* Clear Interrupt */
+ TSN_WR32(feqn, ioaddr + MTL_EST_FRM_SZ_ERR);
+
+ err_stat->hlbf_sz[hbfq] = hbfs;
+ }
+
+ if (status & MTL_EST_STATUS_BTRE) {
+ if ((status & MTL_EST_STATUS_BTRL) ==
+ MTL_EST_STATUS_BTRL_MAX)
+ err_stat->btre_max_n++;
+ else
+ err_stat->btre_n++;
+
+ err_stat->btrl = (status & MTL_EST_STATUS_BTRL) >>
+ MTL_EST_STATUS_BTRL_SHIFT;
+
+ TSN_WR32(MTL_EST_STATUS_BTRE, ioaddr +
+ MTL_EST_STATUS);
+ }
+
+ if (status & MTL_EST_STATUS_SWLC) {
+ TSN_WR32(MTL_EST_STATUS_SWLC, ioaddr +
+ MTL_EST_STATUS);
+ TSN_INFO_NA("SWOL has been switched\n");
+ }
+
+ return status;
+}
+
+int dwmac_get_est_err_stat(struct tsn_err_stat **err_stat)
+{
+ if (!dw_tsn_feat_en[TSN_FEAT_ID_EST])
+ return -ENOTSUPP;
+
+ *err_stat = &dw_err_stat;
+
+ return 0;
+}
+
+int dwmac_clr_est_err_stat(void *ioaddr)
+{
+ if (!dw_tsn_feat_en[TSN_FEAT_ID_EST])
+ return -ENOTSUPP;
+
+ memset(&dw_err_stat, 0, sizeof(dw_err_stat));
+
+ return 0;
+}
@@ -37,9 +37,30 @@
#define MTL_EST_STATUS_BTRL_MAX (0xF << 8)
#define MTL_EST_STATUS_SWOL BIT(7) /* SW owned list */
#define MTL_EST_STATUS_SWOL_SHIFT 7
+#define MTL_EST_STATUS_CGCE BIT(4) /* Constant gate ctrl err */
+#define MTL_EST_STATUS_HLBS BIT(3) /* HLB due to scheduling */
+#define MTL_EST_STATUS_HLBF BIT(2) /* HLB due to frame size */
#define MTL_EST_STATUS_BTRE BIT(1) /* BTR Error */
#define MTL_EST_STATUS_SWLC BIT(0) /* Switch to SWOL complete */
+/* MTL EST Scheduling error */
+#define MTL_EST_SCH_ERR 0x00000c60
+#define MTL_EST_FRM_SZ_ERR 0x00000c64
+#define MTL_EST_FRM_SZ_CAP 0x00000c68
+#define MTL_EST_FRM_SZ_CAP_HBFS_MASK GENMASK(14, 0)
+#define MTL_EST_FRM_SZ_CAP_HBFQ_SHIFT 16
+#define MTL_EST_FRM_SZ_CAP_HBFQ_MASK(x) (x > 4 ? GENMASK(18, 16) : \
+ x > 2 ? GENMASK(17, 16) : \
+ BIT(16))
+
+/* MTL EST interrupt enable */
+#define MTL_EST_INT_EN 0x00000c70
+#define MTL_EST_INT_EN_CGCE BIT(4)
+#define MTL_EST_INT_EN_IEHS BIT(3)
+#define MTL_EST_INT_EN_IEHF BIT(2)
+#define MTL_EST_INT_EN_IEBE BIT(1)
+#define MTL_EST_INT_EN_IECC BIT(0)
+
/* MTL EST GCL control register */
#define MTL_EST_GCL_CTRL 0x00000c80
#define MTL_EST_GCL_CTRL_ADDR GENMASK(10, 8) /* GCL Address */
@@ -118,6 +139,26 @@ struct tsn_hw_cap {
unsigned int ext_max; /* Max time extension */
};
+/* TSN Error Status */
+struct tsn_err_stat {
+ unsigned int cgce_n; /* Constant gate error
+ * count.
+ */
+ unsigned int hlbs_q; /* Queue with HLB due to
+ * Scheduling
+ */
+ unsigned int hlbf_sz[MTL_MAX_TX_QUEUES];/* Frame size that causes
+ * HLB
+ */
+ unsigned int btre_n; /* BTR error with BTR
+ * renewal
+ */
+ unsigned int btre_max_n; /* BTR error with BTR
+ * renewal fail count
+ */
+ unsigned int btrl; /* BTR error loop count */
+};
+
/* EST Gate Control Entry */
struct est_gc_entry {
unsigned int gates; /* gate control: 0: closed,
@@ -150,6 +191,7 @@ struct est_gc_config {
/* TSN functions */
void dwmac_tsn_init(void *ioaddr);
+void dwmac_tsn_setup(void *ioaddr);
void dwmac_get_tsn_hwcap(struct tsn_hw_cap **tsn_hwcap);
void dwmac_set_est_gcb(struct est_gc_entry *gcl, unsigned int bank);
void dwmac_set_tsn_feat(enum tsn_feat_id featid, bool enable);
@@ -170,4 +212,7 @@ int dwmac_set_est_gcrr_times(void *ioaddr,
int dwmac_set_est_enable(void *ioaddr, bool enable);
int dwmac_get_est_gcc(void *ioaddr,
struct est_gc_config **gcc, bool frmdrv);
+int dwmac_est_irq_status(void *ioaddr);
+int dwmac_get_est_err_stat(struct tsn_err_stat **err_stat);
+int dwmac_clr_est_err_stat(void *ioaddr);
#endif /* __DW_TSN_LIB_H__ */
@@ -830,6 +830,9 @@ static void dwmac4_set_mac_loopback(void __iomem *ioaddr, bool enable)
.set_est_gcrr_times = dwmac_set_est_gcrr_times,
.set_est_enable = dwmac_set_est_enable,
.get_est_gcc = dwmac_get_est_gcc,
+ .est_irq_status = dwmac_est_irq_status,
+ .get_est_err_stat = dwmac_get_est_err_stat,
+ .clr_est_err_stat = dwmac_clr_est_err_stat,
.safety_feat_config = dwmac5_safety_feat_config,
.safety_feat_irq_status = dwmac5_safety_feat_irq_status,
.safety_feat_dump = dwmac5_safety_feat_dump,
@@ -337,6 +337,9 @@ struct stmmac_ops {
int (*set_est_enable)(void __iomem *ioaddr, bool enable);
int (*get_est_gcc)(void __iomem *ioaddr,
struct est_gc_config **gcc, bool frmdrv);
+ int (*est_irq_status)(void __iomem *ioaddr);
+ int (*get_est_err_stat)(struct tsn_err_stat **err_stat);
+ int (*clr_est_err_stat)(void __iomem *ioaddr);
/* Safety Features */
int (*safety_feat_config)(void __iomem *ioaddr, unsigned int asp);
int (*safety_feat_irq_status)(struct net_device *ndev,
@@ -437,6 +440,12 @@ struct stmmac_ops {
stmmac_do_callback(__priv, mac, set_est_enable, __args)
#define stmmac_get_est_gcc(__priv, __args...) \
stmmac_do_callback(__priv, mac, get_est_gcc, __args)
+#define stmmac_est_irq_status(__priv, __args...) \
+ stmmac_do_callback(__priv, mac, est_irq_status, __args)
+#define stmmac_get_est_err_stat(__priv, __args...) \
+ stmmac_do_callback(__priv, mac, get_est_err_stat, __args)
+#define stmmac_clr_est_err_stat(__priv, __args...) \
+ stmmac_do_callback(__priv, mac, clr_est_err_stat, __args)
#define stmmac_safety_feat_config(__priv, __args...) \
stmmac_do_callback(__priv, mac, safety_feat_config, __args)
#define stmmac_safety_feat_irq_status(__priv, __args...) \
@@ -2530,6 +2530,9 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
/* Start the ball rolling... */
stmmac_start_all_dma(priv);
+ /* Setup for TSN capability */
+ dwmac_tsn_setup(priv->ioaddr);
+
return 0;
}
@@ -3693,6 +3696,9 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
queue);
}
+ if (priv->hw->tsn_cap & TSN_CAP_EST)
+ stmmac_est_irq_status(priv, priv->ioaddr);
+
/* PCS link status */
if (priv->hw->pcs) {
if (priv->xstats.pcs_link)
@@ -4297,6 +4303,7 @@ int stmmac_dvr_probe(struct device *device,
stmmac_get_tsn_hwcap(priv, &tsn_hwcap);
if (tsn_hwcap && tsn_hwcap->est_support && priv->plat->tsn_est_en) {
stmmac_set_tsn_feat(priv, TSN_FEAT_ID_EST, true);
+ priv->hw->tsn_cap |= TSN_CAP_EST;
dev_info(priv->device, "EST feature enabled\n");
}