net: macb: add support for high speed interface
authorParshuram Thombare <pthombar@cadence.com>
Thu, 29 Oct 2020 12:47:07 +0000 (13:47 +0100)
committerJakub Kicinski <kuba@kernel.org>
Sat, 31 Oct 2020 23:13:20 +0000 (16:13 -0700)
This patch adds support for 10GBASE-R interface to the linux driver for
Cadence's ethernet controller.
This controller has separate MAC's and PCS'es for low and high speed paths.
High speed PCS supports 100M, 1G, 2.5G, 5G and 10G through rate adaptation
implementation. However, since it doesn't support auto negotiation, linux
driver is modified to support 10GBASE-R instead of USXGMII.

Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
Link: https://lore.kernel.org/r/1603975627-18338-1-git-send-email-pthombar@cadence.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/cadence/macb.h
drivers/net/ethernet/cadence/macb_main.c

index 5de47f6..1f5da4e 100644 (file)
 #define MACB_RBQPH             0x04D4
 
 /* GEM register offsets. */
+#define GEM_NCR                        0x0000 /* Network Control */
 #define GEM_NCFGR              0x0004 /* Network Config */
 #define GEM_USRIO              0x000c /* User IO */
 #define GEM_DMACFG             0x0010 /* DMA Configuration */
 #define GEM_JML                        0x0048 /* Jumbo Max Length */
+#define GEM_HS_MAC_CONFIG      0x0050 /* GEM high speed config */
 #define GEM_HRB                        0x0080 /* Hash Bottom */
 #define GEM_HRT                        0x0084 /* Hash Top */
 #define GEM_SA1B               0x0088 /* Specific1 Bottom */
 #define GEM_DCFG7              0x0298 /* Design Config 7 */
 #define GEM_DCFG8              0x029C /* Design Config 8 */
 #define GEM_DCFG10             0x02A4 /* Design Config 10 */
+#define GEM_DCFG12             0x02AC /* Design Config 12 */
+#define GEM_USX_CONTROL                0x0A80 /* High speed PCS control register */
+#define GEM_USX_STATUS         0x0A88 /* High speed PCS status register */
 
 #define GEM_TXBDCTRL   0x04cc /* TX Buffer Descriptor control register */
 #define GEM_RXBDCTRL   0x04d0 /* RX Buffer Descriptor control register */
 #define MACB_IRXFCS_OFFSET     19
 #define MACB_IRXFCS_SIZE       1
 
+/* GEM specific NCR bitfields. */
+#define GEM_ENABLE_HS_MAC_OFFSET       31
+#define GEM_ENABLE_HS_MAC_SIZE         1
+
 /* GEM specific NCFGR bitfields. */
+#define GEM_FD_OFFSET          1 /* Full duplex */
+#define GEM_FD_SIZE            1
 #define GEM_GBE_OFFSET         10 /* Gigabit mode enable */
 #define GEM_GBE_SIZE           1
 #define GEM_PCSSEL_OFFSET      11
 #define GEM_PCSSEL_SIZE                1
+#define GEM_PAE_OFFSET         13 /* Pause enable */
+#define GEM_PAE_SIZE           1
 #define GEM_CLK_OFFSET         18 /* MDC clock division */
 #define GEM_CLK_SIZE           3
 #define GEM_DBW_OFFSET         21 /* Data bus width */
 #define MACB_REV_OFFSET                                0
 #define MACB_REV_SIZE                          16
 
+/* Bitfield in HS_MAC_CONFIG */
+#define GEM_HS_MAC_SPEED_OFFSET                        0
+#define GEM_HS_MAC_SPEED_SIZE                  3
+
 /* Bitfields in DCFG1. */
 #define GEM_IRQCOR_OFFSET                      23
 #define GEM_IRQCOR_SIZE                                1
 #define GEM_DBWDEF_OFFSET                      25
 #define GEM_DBWDEF_SIZE                                3
+#define GEM_NO_PCS_OFFSET                      0
+#define GEM_NO_PCS_SIZE                                1
 
 /* Bitfields in DCFG2. */
 #define GEM_RX_PKT_BUFF_OFFSET                 20
 #define GEM_RXBD_RDBUFF_OFFSET                 8
 #define GEM_RXBD_RDBUFF_SIZE                   4
 
+/* Bitfields in DCFG12. */
+#define GEM_HIGH_SPEED_OFFSET                  26
+#define GEM_HIGH_SPEED_SIZE                    1
+
+/* Bitfields in USX_CONTROL. */
+#define GEM_USX_CTRL_SPEED_OFFSET              14
+#define GEM_USX_CTRL_SPEED_SIZE                        3
+#define GEM_SERDES_RATE_OFFSET                 12
+#define GEM_SERDES_RATE_SIZE                   2
+#define GEM_RX_SCR_BYPASS_OFFSET               9
+#define GEM_RX_SCR_BYPASS_SIZE                 1
+#define GEM_TX_SCR_BYPASS_OFFSET               8
+#define GEM_TX_SCR_BYPASS_SIZE                 1
+#define GEM_TX_EN_OFFSET                       1
+#define GEM_TX_EN_SIZE                         1
+#define GEM_SIGNAL_OK_OFFSET                   0
+#define GEM_SIGNAL_OK_SIZE                     1
+
+/* Bitfields in USX_STATUS. */
+#define GEM_USX_BLOCK_LOCK_OFFSET              0
+#define GEM_USX_BLOCK_LOCK_SIZE                        1
+
 /* Bitfields in TISUBN */
 #define GEM_SUBNSINCR_OFFSET                   0
 #define GEM_SUBNSINCRL_OFFSET                  24
 #define MACB_CAPS_GIGABIT_MODE_AVAILABLE       0x20000000
 #define MACB_CAPS_SG_DISABLED                  0x40000000
 #define MACB_CAPS_MACB_IS_GEM                  0x80000000
+#define MACB_CAPS_PCS                          0x01000000
+#define MACB_CAPS_HIGH_SPEED                   0x02000000
 
 /* LSO settings */
 #define MACB_LSO_UFO_ENABLE                    0x01
@@ -1201,6 +1244,7 @@ struct macb {
        struct mii_bus          *mii_bus;
        struct phylink          *phylink;
        struct phylink_config   phylink_config;
+       struct phylink_pcs      phylink_pcs;
 
        u32                     caps;
        unsigned int            dma_burst_length;
index 883e47c..b7bc160 100644 (file)
@@ -84,6 +84,9 @@ struct sifive_fu540_macb_mgmt {
 #define MACB_WOL_HAS_MAGIC_PACKET      (0x1 << 0)
 #define MACB_WOL_ENABLED               (0x1 << 1)
 
+#define HS_SPEED_10000M                        4
+#define MACB_SERDES_RATE_10G           1
+
 /* Graceful stop timeouts in us. We should allow up to
  * 1 frame time (10 Mbits/s, full-duplex, ignoring collisions)
  */
@@ -513,6 +516,7 @@ static void macb_validate(struct phylink_config *config,
            state->interface != PHY_INTERFACE_MODE_RMII &&
            state->interface != PHY_INTERFACE_MODE_GMII &&
            state->interface != PHY_INTERFACE_MODE_SGMII &&
+           state->interface != PHY_INTERFACE_MODE_10GBASER &&
            !phy_interface_mode_is_rgmii(state->interface)) {
                bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
                return;
@@ -525,10 +529,31 @@ static void macb_validate(struct phylink_config *config,
                return;
        }
 
+       if (state->interface == PHY_INTERFACE_MODE_10GBASER &&
+           !(bp->caps & MACB_CAPS_HIGH_SPEED &&
+             bp->caps & MACB_CAPS_PCS)) {
+               bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+               return;
+       }
+
        phylink_set_port_modes(mask);
        phylink_set(mask, Autoneg);
        phylink_set(mask, Asym_Pause);
 
+       if (bp->caps & MACB_CAPS_GIGABIT_MODE_AVAILABLE &&
+           (state->interface == PHY_INTERFACE_MODE_NA ||
+            state->interface == PHY_INTERFACE_MODE_10GBASER)) {
+               phylink_set(mask, 10000baseCR_Full);
+               phylink_set(mask, 10000baseER_Full);
+               phylink_set(mask, 10000baseKR_Full);
+               phylink_set(mask, 10000baseLR_Full);
+               phylink_set(mask, 10000baseLRM_Full);
+               phylink_set(mask, 10000baseSR_Full);
+               phylink_set(mask, 10000baseT_Full);
+               if (state->interface != PHY_INTERFACE_MODE_NA)
+                       goto out;
+       }
+
        phylink_set(mask, 10baseT_Half);
        phylink_set(mask, 10baseT_Full);
        phylink_set(mask, 100baseT_Half);
@@ -545,23 +570,80 @@ static void macb_validate(struct phylink_config *config,
                if (!(bp->caps & MACB_CAPS_NO_GIGABIT_HALF))
                        phylink_set(mask, 1000baseT_Half);
        }
-
+out:
        bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
        bitmap_and(state->advertising, state->advertising, mask,
                   __ETHTOOL_LINK_MODE_MASK_NBITS);
 }
 
-static void macb_mac_pcs_get_state(struct phylink_config *config,
+static void macb_usx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+                                phy_interface_t interface, int speed,
+                                int duplex)
+{
+       struct macb *bp = container_of(pcs, struct macb, phylink_pcs);
+       u32 config;
+
+       config = gem_readl(bp, USX_CONTROL);
+       config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_10G, config);
+       config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_10000M, config);
+       config &= ~(GEM_BIT(TX_SCR_BYPASS) | GEM_BIT(RX_SCR_BYPASS));
+       config |= GEM_BIT(TX_EN);
+       gem_writel(bp, USX_CONTROL, config);
+}
+
+static void macb_usx_pcs_get_state(struct phylink_pcs *pcs,
                                   struct phylink_link_state *state)
+{
+       struct macb *bp = container_of(pcs, struct macb, phylink_pcs);
+       u32 val;
+
+       state->speed = SPEED_10000;
+       state->duplex = 1;
+       state->an_complete = 1;
+
+       val = gem_readl(bp, USX_STATUS);
+       state->link = !!(val & GEM_BIT(USX_BLOCK_LOCK));
+       val = gem_readl(bp, NCFGR);
+       if (val & GEM_BIT(PAE))
+               state->pause = MLO_PAUSE_RX;
+}
+
+static int macb_usx_pcs_config(struct phylink_pcs *pcs,
+                              unsigned int mode,
+                              phy_interface_t interface,
+                              const unsigned long *advertising,
+                              bool permit_pause_to_mac)
+{
+       struct macb *bp = container_of(pcs, struct macb, phylink_pcs);
+
+       gem_writel(bp, USX_CONTROL, gem_readl(bp, USX_CONTROL) |
+                  GEM_BIT(SIGNAL_OK));
+
+       return 0;
+}
+
+static void macb_pcs_get_state(struct phylink_pcs *pcs,
+                              struct phylink_link_state *state)
 {
        state->link = 0;
 }
 
-static void macb_mac_an_restart(struct phylink_config *config)
+static void macb_pcs_an_restart(struct phylink_pcs *pcs)
 {
        /* Not supported */
 }
 
+static const struct phylink_pcs_ops macb_phylink_usx_pcs_ops = {
+       .pcs_get_state = macb_usx_pcs_get_state,
+       .pcs_config = macb_usx_pcs_config,
+       .pcs_link_up = macb_usx_pcs_link_up,
+};
+
+static const struct phylink_pcs_ops macb_phylink_pcs_ops = {
+       .pcs_get_state = macb_pcs_get_state,
+       .pcs_an_restart = macb_pcs_an_restart,
+};
+
 static void macb_mac_config(struct phylink_config *config, unsigned int mode,
                            const struct phylink_link_state *state)
 {
@@ -569,25 +651,35 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode,
        struct macb *bp = netdev_priv(ndev);
        unsigned long flags;
        u32 old_ctrl, ctrl;
+       u32 old_ncr, ncr;
 
        spin_lock_irqsave(&bp->lock, flags);
 
        old_ctrl = ctrl = macb_or_gem_readl(bp, NCFGR);
+       old_ncr = ncr = macb_or_gem_readl(bp, NCR);
 
        if (bp->caps & MACB_CAPS_MACB_IS_EMAC) {
                if (state->interface == PHY_INTERFACE_MODE_RMII)
                        ctrl |= MACB_BIT(RM9200_RMII);
        } else if (macb_is_gem(bp)) {
                ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL));
+               ncr &= ~GEM_BIT(ENABLE_HS_MAC);
 
-               if (state->interface == PHY_INTERFACE_MODE_SGMII)
+               if (state->interface == PHY_INTERFACE_MODE_SGMII) {
                        ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL);
+               } else if (state->interface == PHY_INTERFACE_MODE_10GBASER) {
+                       ctrl |= GEM_BIT(PCSSEL);
+                       ncr |= GEM_BIT(ENABLE_HS_MAC);
+               }
        }
 
        /* Apply the new configuration, if any */
        if (old_ctrl ^ ctrl)
                macb_or_gem_writel(bp, NCFGR, ctrl);
 
+       if (old_ncr ^ ncr)
+               macb_or_gem_writel(bp, NCR, ncr);
+
        spin_unlock_irqrestore(&bp->lock, flags);
 }
 
@@ -664,6 +756,10 @@ static void macb_mac_link_up(struct phylink_config *config,
 
        macb_or_gem_writel(bp, NCFGR, ctrl);
 
+       if (bp->phy_interface == PHY_INTERFACE_MODE_10GBASER)
+               gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_10000M,
+                                                       gem_readl(bp, HS_MAC_CONFIG)));
+
        spin_unlock_irqrestore(&bp->lock, flags);
 
        /* Enable Rx and Tx */
@@ -672,10 +768,25 @@ static void macb_mac_link_up(struct phylink_config *config,
        netif_tx_wake_all_queues(ndev);
 }
 
+static int macb_mac_prepare(struct phylink_config *config, unsigned int mode,
+                           phy_interface_t interface)
+{
+       struct net_device *ndev = to_net_dev(config->dev);
+       struct macb *bp = netdev_priv(ndev);
+
+       if (interface == PHY_INTERFACE_MODE_10GBASER)
+               bp->phylink_pcs.ops = &macb_phylink_usx_pcs_ops;
+       else
+               bp->phylink_pcs.ops = &macb_phylink_pcs_ops;
+
+       phylink_set_pcs(bp->phylink, &bp->phylink_pcs);
+
+       return 0;
+}
+
 static const struct phylink_mac_ops macb_phylink_ops = {
        .validate = macb_validate,
-       .mac_pcs_get_state = macb_mac_pcs_get_state,
-       .mac_an_restart = macb_mac_an_restart,
+       .mac_prepare = macb_mac_prepare,
        .mac_config = macb_mac_config,
        .mac_link_down = macb_mac_link_down,
        .mac_link_up = macb_mac_link_up,
@@ -3523,6 +3634,11 @@ static void macb_configure_caps(struct macb *bp,
                dcfg = gem_readl(bp, DCFG1);
                if (GEM_BFEXT(IRQCOR, dcfg) == 0)
                        bp->caps |= MACB_CAPS_ISR_CLEAR_ON_WRITE;
+               if (GEM_BFEXT(NO_PCS, dcfg) == 0)
+                       bp->caps |= MACB_CAPS_PCS;
+               dcfg = gem_readl(bp, DCFG12);
+               if (GEM_BFEXT(HIGH_SPEED, dcfg) == 1)
+                       bp->caps |= MACB_CAPS_HIGH_SPEED;
                dcfg = gem_readl(bp, DCFG2);
                if ((dcfg & (GEM_BIT(RX_PKT_BUFF) | GEM_BIT(TX_PKT_BUFF))) == 0)
                        bp->caps |= MACB_CAPS_FIFO_MODE;