fsl/fman: implement several errata workarounds
authorFlorinel Iordache <florinel.iordache@nxp.com>
Thu, 10 Aug 2017 13:47:04 +0000 (16:47 +0300)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Aug 2017 21:37:36 +0000 (14:37 -0700)
Implemented workarounds for the following dTSEC Erratum:
A002, A004, A0012, A0014, A004839 on several operations
that involve MAC CFG register changes: adjust link,
rx pause frames, modify MAC address.

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
Acked-by: Madalin Bucur <madalin.bucur@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/freescale/fman/fman_dtsec.c

index 98bba10fc38c1a5916108fc0ec4b1f6f136f0032..ea43b497414986c55d07ce9b175082202f951044 100644 (file)
 #define DTSEC_ECNTRL_R100M             0x00000008
 #define DTSEC_ECNTRL_QSGMIIM           0x00000001
 
-#define DTSEC_TCTRL_GTS                        0x00000020
+#define TCTRL_GTS                      0x00000020
 
 #define RCTRL_PAL_MASK                 0x001f0000
 #define RCTRL_PAL_SHIFT                        16
@@ -863,6 +863,52 @@ int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val)
        return 0;
 }
 
+static void graceful_start(struct fman_mac *dtsec, enum comm_mode mode)
+{
+       struct dtsec_regs __iomem *regs = dtsec->regs;
+
+       if (mode & COMM_MODE_TX)
+               iowrite32be(ioread32be(&regs->tctrl) &
+                               ~TCTRL_GTS, &regs->tctrl);
+       if (mode & COMM_MODE_RX)
+               iowrite32be(ioread32be(&regs->rctrl) &
+                               ~RCTRL_GRS, &regs->rctrl);
+}
+
+static void graceful_stop(struct fman_mac *dtsec, enum comm_mode mode)
+{
+       struct dtsec_regs __iomem *regs = dtsec->regs;
+       u32 tmp;
+
+       /* Graceful stop - Assert the graceful Rx stop bit */
+       if (mode & COMM_MODE_RX) {
+               tmp = ioread32be(&regs->rctrl) | RCTRL_GRS;
+               iowrite32be(tmp, &regs->rctrl);
+
+               if (dtsec->fm_rev_info.major == 2) {
+                       /* Workaround for dTSEC Errata A002 */
+                       usleep_range(100, 200);
+               } else {
+                       /* Workaround for dTSEC Errata A004839 */
+                       usleep_range(10, 50);
+               }
+       }
+
+       /* Graceful stop - Assert the graceful Tx stop bit */
+       if (mode & COMM_MODE_TX) {
+               if (dtsec->fm_rev_info.major == 2) {
+                       /* dTSEC Errata A004: Do not use TCTRL[GTS]=1 */
+                       pr_debug("GTS not supported due to DTSEC_A004 Errata.\n");
+               } else {
+                       tmp = ioread32be(&regs->tctrl) | TCTRL_GTS;
+                       iowrite32be(tmp, &regs->tctrl);
+
+                       /* Workaround for dTSEC Errata A0012, A0014 */
+                       usleep_range(10, 50);
+               }
+       }
+}
+
 int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode)
 {
        struct dtsec_regs __iomem *regs = dtsec->regs;
@@ -880,13 +926,8 @@ int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode)
 
        iowrite32be(tmp, &regs->maccfg1);
 
-       /* Graceful start - clear the graceful receive stop bit */
-       if (mode & COMM_MODE_TX)
-               iowrite32be(ioread32be(&regs->tctrl) & ~DTSEC_TCTRL_GTS,
-                           &regs->tctrl);
-       if (mode & COMM_MODE_RX)
-               iowrite32be(ioread32be(&regs->rctrl) & ~RCTRL_GRS,
-                           &regs->rctrl);
+       /* Graceful start - clear the graceful Rx/Tx stop bit */
+       graceful_start(dtsec, mode);
 
        return 0;
 }
@@ -899,23 +940,8 @@ int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode)
        if (!is_init_done(dtsec->dtsec_drv_param))
                return -EINVAL;
 
-       /* Gracefull stop - Assert the graceful transmit stop bit */
-       if (mode & COMM_MODE_RX) {
-               tmp = ioread32be(&regs->rctrl) | RCTRL_GRS;
-               iowrite32be(tmp, &regs->rctrl);
-
-               if (dtsec->fm_rev_info.major == 2)
-                       usleep_range(100, 200);
-               else
-                       udelay(10);
-       }
-
-       if (mode & COMM_MODE_TX) {
-               if (dtsec->fm_rev_info.major == 2)
-                       pr_debug("GTS not supported due to DTSEC_A004 errata.\n");
-               else
-                       pr_debug("GTS not supported due to DTSEC_A0014 errata.\n");
-       }
+       /* Graceful stop - Assert the graceful Rx/Tx stop bit */
+       graceful_stop(dtsec, mode);
 
        tmp = ioread32be(&regs->maccfg1);
        if (mode & COMM_MODE_RX)
@@ -933,11 +959,19 @@ int dtsec_set_tx_pause_frames(struct fman_mac *dtsec,
                              u16 pause_time, u16 __maybe_unused thresh_time)
 {
        struct dtsec_regs __iomem *regs = dtsec->regs;
+       enum comm_mode mode = COMM_MODE_NONE;
        u32 ptv = 0;
 
        if (!is_init_done(dtsec->dtsec_drv_param))
                return -EINVAL;
 
+       if ((ioread32be(&regs->rctrl) & RCTRL_GRS) == 0)
+               mode |= COMM_MODE_RX;
+       if ((ioread32be(&regs->tctrl) & TCTRL_GTS) == 0)
+               mode |= COMM_MODE_TX;
+
+       graceful_stop(dtsec, mode);
+
        if (pause_time) {
                /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */
                if (dtsec->fm_rev_info.major == 2 && pause_time <= 320) {
@@ -958,17 +992,27 @@ int dtsec_set_tx_pause_frames(struct fman_mac *dtsec,
                iowrite32be(ioread32be(&regs->maccfg1) & ~MACCFG1_TX_FLOW,
                            &regs->maccfg1);
 
+       graceful_start(dtsec, mode);
+
        return 0;
 }
 
 int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en)
 {
        struct dtsec_regs __iomem *regs = dtsec->regs;
+       enum comm_mode mode = COMM_MODE_NONE;
        u32 tmp;
 
        if (!is_init_done(dtsec->dtsec_drv_param))
                return -EINVAL;
 
+       if ((ioread32be(&regs->rctrl) & RCTRL_GRS) == 0)
+               mode |= COMM_MODE_RX;
+       if ((ioread32be(&regs->tctrl) & TCTRL_GTS) == 0)
+               mode |= COMM_MODE_TX;
+
+       graceful_stop(dtsec, mode);
+
        tmp = ioread32be(&regs->maccfg1);
        if (en)
                tmp |= MACCFG1_RX_FLOW;
@@ -976,20 +1020,34 @@ int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en)
                tmp &= ~MACCFG1_RX_FLOW;
        iowrite32be(tmp, &regs->maccfg1);
 
+       graceful_start(dtsec, mode);
+
        return 0;
 }
 
 int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr)
 {
+       struct dtsec_regs __iomem *regs = dtsec->regs;
+       enum comm_mode mode = COMM_MODE_NONE;
+
        if (!is_init_done(dtsec->dtsec_drv_param))
                return -EINVAL;
 
+       if ((ioread32be(&regs->rctrl) & RCTRL_GRS) == 0)
+               mode |= COMM_MODE_RX;
+       if ((ioread32be(&regs->tctrl) & TCTRL_GTS) == 0)
+               mode |= COMM_MODE_TX;
+
+       graceful_stop(dtsec, mode);
+
        /* Initialize MAC Station Address registers (1 & 2)
         * Station address have to be swapped (big endian to little endian
         */
        dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr);
        set_mac_address(dtsec->regs, (u8 *)(*enet_addr));
 
+       graceful_start(dtsec, mode);
+
        return 0;
 }
 
@@ -1162,11 +1220,19 @@ int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val)
 int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed)
 {
        struct dtsec_regs __iomem *regs = dtsec->regs;
+       enum comm_mode mode = COMM_MODE_NONE;
        u32 tmp;
 
        if (!is_init_done(dtsec->dtsec_drv_param))
                return -EINVAL;
 
+       if ((ioread32be(&regs->rctrl) & RCTRL_GRS) == 0)
+               mode |= COMM_MODE_RX;
+       if ((ioread32be(&regs->tctrl) & TCTRL_GTS) == 0)
+               mode |= COMM_MODE_TX;
+
+       graceful_stop(dtsec, mode);
+
        tmp = ioread32be(&regs->maccfg2);
 
        /* Full Duplex */
@@ -1186,6 +1252,8 @@ int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed)
                tmp &= ~DTSEC_ECNTRL_R100M;
        iowrite32be(tmp, &regs->ecntrl);
 
+       graceful_start(dtsec, mode);
+
        return 0;
 }