Extract error information from rx and tx buffer descriptors,
and update error counters.

Signed-off-by: Mathias Thore <mathias.th...@infinera.com>
---
 drivers/net/wan/fsl_ucc_hdlc.c | 51 +++++++++++++++++++++++++++++-----
 include/soc/fsl/qe/ucc_fast.h  |  8 +++++-
 2 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c
index 5f0366a125e2..934fc149c6b3 100644
--- a/drivers/net/wan/fsl_ucc_hdlc.c
+++ b/drivers/net/wan/fsl_ucc_hdlc.c
@@ -36,6 +36,7 @@
 #define DRV_NAME "ucc_hdlc"
 
 #define TDM_PPPOHT_SLIC_MAXIN
+#define RX_BD_ERRORS (R_CD_S | R_OV_S | R_CR_S | R_AB_S | R_NO_S | R_LG_S)
 
 static struct ucc_tdm_info utdm_primary_info = {
        .uf_info = {
@@ -420,12 +421,25 @@ static netdev_tx_t ucc_hdlc_tx(struct sk_buff *skb, 
struct net_device *dev)
        return NETDEV_TX_OK;
 }
 
+static int hdlc_tx_restart(struct ucc_hdlc_private *priv)
+{
+       u32 cecr_subblock;
+
+       cecr_subblock =
+               ucc_fast_get_qe_cr_subblock(priv->ut_info->uf_info.ucc_num);
+
+       qe_issue_cmd(QE_RESTART_TX, cecr_subblock,
+                    QE_CR_PROTOCOL_UNSPECIFIED, 0);
+       return 0;
+}
+
 static int hdlc_tx_done(struct ucc_hdlc_private *priv)
 {
        /* Start from the next BD that should be filled */
        struct net_device *dev = priv->ndev;
        struct qe_bd *bd;               /* BD pointer */
        u16 bd_status;
+       int tx_restart = 0;
 
        bd = priv->dirty_tx;
        bd_status = ioread16be(&bd->status);
@@ -434,6 +448,15 @@ static int hdlc_tx_done(struct ucc_hdlc_private *priv)
        while ((bd_status & T_R_S) == 0) {
                struct sk_buff *skb;
 
+               if (bd_status & T_UN_S) { /* Underrun */
+                       dev->stats.tx_fifo_errors++;
+                       tx_restart = 1;
+               }
+               if (bd_status & T_CT_S) { /* Carrier lost */
+                       dev->stats.tx_carrier_errors++;
+                       tx_restart = 1;
+               }
+
                /* BD contains already transmitted buffer.   */
                /* Handle the transmitted buffer and release */
                /* the BD to be used with the current frame  */
@@ -465,6 +488,9 @@ static int hdlc_tx_done(struct ucc_hdlc_private *priv)
        }
        priv->dirty_tx = bd;
 
+       if (tx_restart)
+               hdlc_tx_restart(priv);
+
        return 0;
 }
 
@@ -483,11 +509,22 @@ static int hdlc_rx_done(struct ucc_hdlc_private *priv, 
int rx_work_limit)
 
        /* while there are received buffers and BD is full (~R_E) */
        while (!((bd_status & (R_E_S)) || (--rx_work_limit < 0))) {
-               if (bd_status & R_OV_S)
-                       dev->stats.rx_over_errors++;
-               if (bd_status & R_CR_S) {
-                       dev->stats.rx_crc_errors++;
-                       dev->stats.rx_dropped++;
+               if (bd_status & (RX_BD_ERRORS)) {
+                       dev->stats.rx_errors++;
+
+                       if (bd_status & R_CD_S)
+                               dev->stats.collisions++;
+                       if (bd_status & R_OV_S)
+                               dev->stats.rx_fifo_errors++;
+                       if (bd_status & R_CR_S)
+                               dev->stats.rx_crc_errors++;
+                       if (bd_status & R_AB_S)
+                               dev->stats.rx_over_errors++;
+                       if (bd_status & R_NO_S)
+                               dev->stats.rx_frame_errors++;
+                       if (bd_status & R_LG_S)
+                               dev->stats.rx_length_errors++;
+
                        goto recycle;
                }
                bdbuffer = priv->rx_buffer +
@@ -535,7 +572,7 @@ static int hdlc_rx_done(struct ucc_hdlc_private *priv, int 
rx_work_limit)
                netif_receive_skb(skb);
 
 recycle:
-               iowrite16be(bd_status | R_E_S | R_I_S, &bd->status);
+               iowrite16be((bd_status & R_W_S) | R_E_S | R_I_S, &bd->status);
 
                /* update to point at the next bd */
                if (bd_status & R_W_S) {
@@ -611,7 +648,7 @@ static irqreturn_t ucc_hdlc_irq_handler(int irq, void 
*dev_id)
 
        /* Errors and other events */
        if (ucce >> 16 & UCC_HDLC_UCCE_BSY)
-               dev->stats.rx_errors++;
+               dev->stats.rx_missed_errors++;
        if (ucce >> 16 & UCC_HDLC_UCCE_TXE)
                dev->stats.tx_errors++;
 
diff --git a/include/soc/fsl/qe/ucc_fast.h b/include/soc/fsl/qe/ucc_fast.h
index 3ee9e7c1a7d7..dcd6b865b590 100644
--- a/include/soc/fsl/qe/ucc_fast.h
+++ b/include/soc/fsl/qe/ucc_fast.h
@@ -41,8 +41,12 @@
 #define R_L_S  0x0800  /* last */
 #define R_F_S  0x0400  /* first */
 #define R_CM_S 0x0200  /* continuous mode */
+#define R_LG_S  0x0020  /* frame length */
+#define R_NO_S  0x0010  /* nonoctet */
+#define R_AB_S  0x0008  /* abort */
 #define R_CR_S 0x0004  /* crc */
-#define R_OV_S 0x0002  /* crc */
+#define R_OV_S 0x0002  /* overrun */
+#define R_CD_S  0x0001  /* carrier detect */
 
 /* transmit BD's status */
 #define T_R_S  0x8000  /* ready bit */
@@ -51,6 +55,8 @@
 #define T_L_S  0x0800  /* last */
 #define T_TC_S 0x0400  /* crc */
 #define T_TM_S 0x0200  /* continuous mode */
+#define T_UN_S  0x0002  /* hdlc underrun */
+#define T_CT_S  0x0001  /* hdlc carrier lost */
 
 /* Rx Data buffer must be 4 bytes aligned in most cases */
 #define UCC_FAST_RX_ALIGN                      4
-- 
2.18.1

Reply via email to