net: stmmac: socfpga: fix phy and ptp_ref setup for Arria10/Stratix10
authorDinh Nguyen <dinguyen@kernel.org>
Wed, 5 Jun 2019 15:05:51 +0000 (10:05 -0500)
committerDavid S. Miller <davem@davemloft.net>
Thu, 6 Jun 2019 21:21:06 +0000 (14:21 -0700)
On the Arria10, Agilex, and Stratix10 SoC, there are a few differences from
the Cyclone5 and Arria5:
 - The emac PHY setup bits are in separate registers.
 - The PTP reference clock select mask is different.
 - The register to enable the emac signal from FPGA is different.

Thus, this patch creates a separate function for setting the phy modes on
Arria10/Agilex/Stratix10. The separation is based a new DTS binding:
"altr,socfpga-stmmac-a10-s10".

Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c

index 75a6471db76ce742f926f0a9428e76e6808a914f..d939f7b99b94c5d159283e9c228bc55f184d23ea 100644 (file)
 #define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2
 #define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003
 #define SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000010
+#define SYSMGR_GEN10_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000100
 
 #define SYSMGR_FPGAGRP_MODULE_REG  0x00000028
 #define SYSMGR_FPGAGRP_MODULE_EMAC 0x00000004
+#define SYSMGR_FPGAINTF_EMAC_REG       0x00000070
+#define SYSMGR_FPGAINTF_EMAC_BIT       0x1
 
 #define EMAC_SPLITTER_CTRL_REG                 0x0
 #define EMAC_SPLITTER_CTRL_SPEED_MASK          0x3
 #define EMAC_SPLITTER_CTRL_SPEED_100           0x3
 #define EMAC_SPLITTER_CTRL_SPEED_1000          0x0
 
+struct socfpga_dwmac;
+struct socfpga_dwmac_ops {
+       int (*set_phy_mode)(struct socfpga_dwmac *dwmac_priv);
+};
+
 struct socfpga_dwmac {
        int     interface;
        u32     reg_offset;
@@ -59,6 +67,7 @@ struct socfpga_dwmac {
        void __iomem *splitter_base;
        bool f2h_ptp_ref_clk;
        struct tse_pcs pcs;
+       const struct socfpga_dwmac_ops *ops;
 };
 
 static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed)
@@ -233,28 +242,36 @@ err_node_put:
        return ret;
 }
 
-static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac)
+static int socfpga_set_phy_mode_common(int phymode, u32 *val)
 {
-       struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
-       int phymode = dwmac->interface;
-       u32 reg_offset = dwmac->reg_offset;
-       u32 reg_shift = dwmac->reg_shift;
-       u32 ctrl, val, module;
-
        switch (phymode) {
        case PHY_INTERFACE_MODE_RGMII:
        case PHY_INTERFACE_MODE_RGMII_ID:
-               val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
+               *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
                break;
        case PHY_INTERFACE_MODE_MII:
        case PHY_INTERFACE_MODE_GMII:
        case PHY_INTERFACE_MODE_SGMII:
-               val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
+               *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
                break;
        case PHY_INTERFACE_MODE_RMII:
-               val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII;
+               *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII;
                break;
        default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int socfpga_gen5_set_phy_mode(struct socfpga_dwmac *dwmac)
+{
+       struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
+       int phymode = dwmac->interface;
+       u32 reg_offset = dwmac->reg_offset;
+       u32 reg_shift = dwmac->reg_shift;
+       u32 ctrl, val, module;
+
+       if (socfpga_set_phy_mode_common(phymode, &val)) {
                dev_err(dwmac->dev, "bad phy mode %d\n", phymode);
                return -EINVAL;
        }
@@ -305,6 +322,62 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac)
        return 0;
 }
 
+static int socfpga_gen10_set_phy_mode(struct socfpga_dwmac *dwmac)
+{
+       struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
+       int phymode = dwmac->interface;
+       u32 reg_offset = dwmac->reg_offset;
+       u32 reg_shift = dwmac->reg_shift;
+       u32 ctrl, val, module;
+
+       if (socfpga_set_phy_mode_common(phymode, &val))
+               return -EINVAL;
+
+       /* Overwrite val to GMII if splitter core is enabled. The phymode here
+        * is the actual phy mode on phy hardware, but phy interface from
+        * EMAC core is GMII.
+        */
+       if (dwmac->splitter_base)
+               val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
+
+       /* Assert reset to the enet controller before changing the phy mode */
+       reset_control_assert(dwmac->stmmac_ocp_rst);
+       reset_control_assert(dwmac->stmmac_rst);
+
+       regmap_read(sys_mgr_base_addr, reg_offset, &ctrl);
+       ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK);
+       ctrl |= val;
+
+       if (dwmac->f2h_ptp_ref_clk ||
+           phymode == PHY_INTERFACE_MODE_MII ||
+           phymode == PHY_INTERFACE_MODE_GMII ||
+           phymode == PHY_INTERFACE_MODE_SGMII) {
+               ctrl |= SYSMGR_GEN10_EMACGRP_CTRL_PTP_REF_CLK_MASK;
+               regmap_read(sys_mgr_base_addr, SYSMGR_FPGAINTF_EMAC_REG,
+                           &module);
+               module |= (SYSMGR_FPGAINTF_EMAC_BIT << reg_shift);
+               regmap_write(sys_mgr_base_addr, SYSMGR_FPGAINTF_EMAC_REG,
+                            module);
+       } else {
+               ctrl &= ~SYSMGR_GEN10_EMACGRP_CTRL_PTP_REF_CLK_MASK;
+       }
+
+       regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
+
+       /* Deassert reset for the phy configuration to be sampled by
+        * the enet controller, and operation to start in requested mode
+        */
+       reset_control_deassert(dwmac->stmmac_ocp_rst);
+       reset_control_deassert(dwmac->stmmac_rst);
+       if (phymode == PHY_INTERFACE_MODE_SGMII) {
+               if (tse_pcs_init(dwmac->pcs.tse_pcs_base, &dwmac->pcs) != 0) {
+                       dev_err(dwmac->dev, "Unable to initialize TSE PCS");
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
 static int socfpga_dwmac_probe(struct platform_device *pdev)
 {
        struct plat_stmmacenet_data *plat_dat;
@@ -314,6 +387,13 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
        struct socfpga_dwmac    *dwmac;
        struct net_device       *ndev;
        struct stmmac_priv      *stpriv;
+       const struct socfpga_dwmac_ops *ops;
+
+       ops = device_get_match_data(&pdev->dev);
+       if (!ops) {
+               dev_err(&pdev->dev, "no of match data provided\n");
+               return -EINVAL;
+       }
 
        ret = stmmac_get_platform_resources(pdev, &stmmac_res);
        if (ret)
@@ -344,6 +424,7 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
                goto err_remove_config_dt;
        }
 
+       dwmac->ops = ops;
        plat_dat->bsp_priv = dwmac;
        plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed;
 
@@ -360,7 +441,7 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
         */
        dwmac->stmmac_rst = stpriv->plat->stmmac_rst;
 
-       ret = socfpga_dwmac_set_phy_mode(dwmac);
+       ret = ops->set_phy_mode(dwmac);
        if (ret)
                goto err_dvr_remove;
 
@@ -379,8 +460,9 @@ static int socfpga_dwmac_resume(struct device *dev)
 {
        struct net_device *ndev = dev_get_drvdata(dev);
        struct stmmac_priv *priv = netdev_priv(ndev);
+       struct socfpga_dwmac *dwmac_priv = get_stmmac_bsp_priv(dev);
 
-       socfpga_dwmac_set_phy_mode(priv->plat->bsp_priv);
+       dwmac_priv->ops->set_phy_mode(priv->plat->bsp_priv);
 
        /* Before the enet controller is suspended, the phy is suspended.
         * This causes the phy clock to be gated. The enet controller is
@@ -407,8 +489,17 @@ static int socfpga_dwmac_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(socfpga_dwmac_pm_ops, stmmac_suspend,
                                               socfpga_dwmac_resume);
 
+static const struct socfpga_dwmac_ops socfpga_gen5_ops = {
+       .set_phy_mode = socfpga_gen5_set_phy_mode,
+};
+
+static const struct socfpga_dwmac_ops socfpga_gen10_ops = {
+       .set_phy_mode = socfpga_gen10_set_phy_mode,
+};
+
 static const struct of_device_id socfpga_dwmac_match[] = {
-       { .compatible = "altr,socfpga-stmmac" },
+       { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gen5_ops },
+       { .compatible = "altr,socfpga-stmmac-a10-s10", .data = &socfpga_gen10_ops },
        { }
 };
 MODULE_DEVICE_TABLE(of, socfpga_dwmac_match);