This adds support for TSE PCS (Triple Speed Ethernet Physical Coding
Sublayer) that uses SGMII adapter when the phy-mode in device tree is
set to sgmii.

Signed-off-by: Ooi, Joyce <joyce....@intel.com>
---
 arch/arm/mach-socfpga/include/mach/misc.h |    3 +
 arch/arm/mach-socfpga/misc_s10.c          |    6 +
 drivers/net/Makefile                      |    3 +-
 drivers/net/designware.c                  |    5 +
 drivers/net/designware.h                  |    1 +
 drivers/net/dwmac_socfpga.c               |  122 +++++++++++++++++++
 drivers/net/phy/altr_tse_pcs.c            |  184 +++++++++++++++++++++++++++++
 drivers/net/phy/altr_tse_pcs.h            |   56 +++++++++
 8 files changed, 379 insertions(+), 1 deletions(-)
 create mode 100644 drivers/net/phy/altr_tse_pcs.c
 create mode 100644 drivers/net/phy/altr_tse_pcs.h
---
v2: add a __weak function to make it compatible for all socfpga platforms
v3: remove __weak function and use board-specific implementation instead

diff --git a/arch/arm/mach-socfpga/include/mach/misc.h 
b/arch/arm/mach-socfpga/include/mach/misc.h
index 4fc9570..751705e 100644
--- a/arch/arm/mach-socfpga/include/mach/misc.h
+++ b/arch/arm/mach-socfpga/include/mach/misc.h
@@ -30,6 +30,9 @@ void socfpga_init_security_policies(void);
 void socfpga_sdram_remap_zero(void);
 #endif
 
+#ifdef CONFIG_TARGET_SOCFPGA_STRATIX10
+int socfpga_test_fpga_ready(void);
+#endif
 void do_bridge_reset(int enable);
 
 #endif /* _MISC_H_ */
diff --git a/arch/arm/mach-socfpga/misc_s10.c b/arch/arm/mach-socfpga/misc_s10.c
index e599362..3cd9c30 100644
--- a/arch/arm/mach-socfpga/misc_s10.c
+++ b/arch/arm/mach-socfpga/misc_s10.c
@@ -11,6 +11,7 @@
 #include <miiphy.h>
 #include <netdev.h>
 #include <asm/io.h>
+#include <asm/arch/mailbox_s10.h>
 #include <asm/arch/reset_manager.h>
 #include <asm/arch/system_manager.h>
 #include <asm/arch/misc.h>
@@ -91,6 +92,11 @@ static int socfpga_set_phymode(void)
 
        return 0;
 }
+
+int socfpga_test_fpga_ready(void)
+{
+       return mbox_get_fpga_config_status(MBOX_CONFIG_STATUS);
+}
 #else
 static int socfpga_set_phymode(void)
 {
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 48a2878..c2333b5 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -14,7 +14,7 @@ obj-$(CONFIG_CALXEDA_XGMAC) += calxedaxgmac.o
 obj-$(CONFIG_CS8900) += cs8900.o
 obj-$(CONFIG_TULIP) += dc2114x.o
 obj-$(CONFIG_ETH_DESIGNWARE) += designware.o
-obj-$(CONFIG_ETH_DESIGNWARE_SOCFPGA) += dwmac_socfpga.o
+obj-$(CONFIG_ETH_DESIGNWARE_SOCFPGA) += dwmac-socfpga.o
 obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
 obj-$(CONFIG_DNET) += dnet.o
 obj-$(CONFIG_E1000) += e1000.o
@@ -73,3 +73,4 @@ obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o
 obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o
 obj-$(CONFIG_FSL_PFE) += pfe_eth/
 obj-$(CONFIG_SNI_AVE) += sni_ave.o
+dwmac-socfpga-objs := phy/altr_tse_pcs.o dwmac_socfpga.o
diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index 19db0a8..666cf41 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -235,6 +235,8 @@ static int dw_adjust_link(struct dw_eth_dev *priv, struct 
eth_mac_regs *mac_p,
                          struct phy_device *phydev)
 {
        u32 conf = readl(&mac_p->conf) | FRAMEBURSTENABLE | DISABLERXOWN;
+       struct udevice *dev = priv->phydev->dev;
+       struct dw_eth_pdata *dw_pdata = dev_get_platdata(dev);
 
        if (!phydev->link) {
                printf("%s: No link.\n", phydev->dev->name);
@@ -254,6 +256,9 @@ static int dw_adjust_link(struct dw_eth_dev *priv, struct 
eth_mac_regs *mac_p,
 
        writel(conf, &mac_p->conf);
 
+       if (dw_pdata->pcs_adjust_link)
+               dw_pdata->pcs_adjust_link(dev, phydev);
+
        printf("Speed: %d, %s duplex%s\n", phydev->speed,
               (phydev->duplex) ? "full" : "half",
               (phydev->port == PORT_FIBRE) ? ", fiber mode" : "");
diff --git a/drivers/net/designware.h b/drivers/net/designware.h
index dea12b7..3a5a93f 100644
--- a/drivers/net/designware.h
+++ b/drivers/net/designware.h
@@ -255,6 +255,7 @@ extern const struct eth_ops designware_eth_ops;
 struct dw_eth_pdata {
        struct eth_pdata eth_pdata;
        u32 reset_delays[3];
+       void (*pcs_adjust_link)(struct udevice *dev, struct phy_device *phydev);
 };
 
 int designware_eth_init(struct dw_eth_dev *priv, u8 *enetaddr);
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index 08fc967..7d13f3c 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -9,12 +9,16 @@
 #include <asm/io.h>
 #include <dm.h>
 #include <clk.h>
+#include <fdt_support.h>
 #include <phy.h>
 #include <regmap.h>
 #include <reset.h>
 #include <syscon.h>
 #include "designware.h"
+#include "phy/altr_tse_pcs.h"
 
+#include <asm/arch/misc.h>
+#include <asm/arch/reset_manager.h>
 #include <asm/arch/system_manager.h>
 
 enum dwmac_type {
@@ -27,8 +31,123 @@ struct dwmac_socfpga_platdata {
        struct dw_eth_pdata     dw_eth_pdata;
        enum dwmac_type         type;
        void                    *phy_intf;
+       struct tse_pcs          pcs;
 };
 
+static void socfpga_tse_pcs_adjust_link(struct udevice *dev,
+                                       struct phy_device *phydev)
+{
+       struct dwmac_socfpga_platdata *pdata = dev_get_platdata(dev);
+       phys_addr_t tse_pcs_base = pdata->pcs.tse_pcs_base;
+       phys_addr_t sgmii_adapter_base = pdata->pcs.sgmii_adapter_base;
+
+       if ((tse_pcs_base) && (sgmii_adapter_base))
+               writew(SGMII_ADAPTER_DISABLE,
+                      sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG);
+
+       if ((tse_pcs_base) && (sgmii_adapter_base))
+               tse_pcs_fix_mac_speed(&pdata->pcs, phydev, phydev->speed);
+}
+
+static int socfpga_dw_tse_pcs_init(struct udevice *dev)
+{
+       struct dwmac_socfpga_platdata *pdata = dev_get_platdata(dev);
+       struct dw_eth_pdata *dw_pdata = &pdata->dw_eth_pdata;
+       struct eth_pdata *eth_pdata = &pdata->dw_eth_pdata.eth_pdata;
+       phys_addr_t tse_pcs_base = pdata->pcs.tse_pcs_base;
+       phys_addr_t sgmii_adapter_base = pdata->pcs.sgmii_adapter_base;
+       const fdt32_t *reg;
+       int ret = 0;
+       int parent, index, na, ns, offset, len;
+
+       offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev),
+                                      "altr,gmii-to-sgmii-converter");
+       if (offset > 0) {
+#ifdef CONFIG_TARGET_SOCFPGA_STRATIX10
+               /* check FPGA status */
+               ret = socfpga_test_fpga_ready();
+               if (ret) {
+                       debug("%s: FPGA not ready (%d)\n", __func__, ret);
+                       return -EPERM;
+               }
+#endif
+               /* enable HPS bridge */
+               do_bridge_reset(1);
+
+               parent = fdt_parent_offset(gd->fdt_blob, offset);
+               if (parent < 0) {
+                       debug("s10-hps-bridge DT not found\n");
+                       return -ENODEV;
+               }
+
+               na = fdt_address_cells(gd->fdt_blob, parent);
+               if (na < 1) {
+                       debug("bad #address-cells\n");
+                       return -EINVAL;
+               }
+
+               ns = fdt_size_cells(gd->fdt_blob, parent);
+               if (ns < 1) {
+                       debug("bad #size-cells\n");
+                       return -EINVAL;
+               }
+
+               index = fdt_stringlist_search(gd->fdt_blob, offset, "reg-names",
+                                             "eth_tse_control_port");
+               if (index < 0) {
+                       debug("fail to find eth_tse_control_port: %s\n",
+                             fdt_strerror(index));
+                       return -ENOENT;
+               }
+
+               reg = fdt_getprop(gd->fdt_blob, offset, "reg", &len);
+               if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns))))
+                       return -EINVAL;
+
+               reg += index * (na + ns);
+               tse_pcs_base = fdt_translate_address((void *)gd->fdt_blob,
+                                                    offset, reg);
+               if (tse_pcs_base == FDT_ADDR_T_NONE) {
+                       debug("tse pcs address not found\n");
+                       return -EINVAL;
+               }
+
+               index = fdt_stringlist_search(gd->fdt_blob, offset, "reg-names",
+                                             
"gmii_to_sgmii_adapter_avalon_slave");
+               if (index < 0) {
+                       debug("fail to find gmii_to_sgmii_adapter_avalon_slave: 
%s\n",
+                             fdt_strerror(index));
+                       return -EINVAL;
+               }
+
+               reg = fdt_getprop(gd->fdt_blob, offset, "reg", &len);
+               if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns))))
+                       return -EINVAL;
+
+               reg += index * (na + ns);
+               sgmii_adapter_base = fdt_translate_address((void *)gd->fdt_blob,
+                                                          offset, reg);
+               if (sgmii_adapter_base == FDT_ADDR_T_NONE) {
+                       debug("gmii-to-sgmii adapter address not found\n");
+                       return -EINVAL;
+               }
+
+               if (eth_pdata->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+                       if (tse_pcs_init(tse_pcs_base, &pdata->pcs) != 0) {
+                               debug("Unable to intiialize TSE PCS\n");
+                               return -EINVAL;
+                       }
+               }
+               /* Clear sgmii_adapter_base first */
+               writel(0, sgmii_adapter_base);
+
+               pdata->pcs.tse_pcs_base = tse_pcs_base;
+               pdata->pcs.sgmii_adapter_base = sgmii_adapter_base;
+               dw_pdata->pcs_adjust_link = socfpga_tse_pcs_adjust_link;
+       }
+       return ret;
+}
+
 static int dwmac_socfpga_ofdata_to_platdata(struct udevice *dev)
 {
        struct dwmac_socfpga_platdata *pdata = dev_get_platdata(dev);
@@ -94,6 +213,7 @@ static int dwmac_socfpga_probe(struct udevice *dev)
                switch (edata->phy_interface) {
                case PHY_INTERFACE_MODE_MII:
                case PHY_INTERFACE_MODE_GMII:
+               case PHY_INTERFACE_MODE_SGMII:
                        modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
                        break;
                case PHY_INTERFACE_MODE_RMII:
@@ -122,6 +242,8 @@ static int dwmac_socfpga_probe(struct udevice *dev)
                reset_release_bulk(&reset_bulk);
        }
 
+       socfpga_dw_tse_pcs_init(dev);
+
        return designware_eth_probe(dev);
 }
 
diff --git a/drivers/net/phy/altr_tse_pcs.c b/drivers/net/phy/altr_tse_pcs.c
new file mode 100644
index 0000000..4423538
--- /dev/null
+++ b/drivers/net/phy/altr_tse_pcs.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Intel Corporation <www.intel.com>
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#include <command.h>
+#include <malloc.h>
+#include <net.h>
+#include <linux/compat.h>
+#include <phy.h>
+#include <timer.h>
+#include <wait_bit.h>
+#include <linux/ethtool.h>
+
+#include <asm/omap_common.h>
+#include <asm/omap_musb.h>
+
+#include "altr_tse_pcs.h"
+
+static int tse_pcs_reset(phys_addr_t base, struct tse_pcs *pcs)
+{
+       int ret;
+       u16 val;
+       phys_addr_t reg = base + TSE_PCS_CONTROL_REG;
+
+       val = readw(reg);
+       val |= TSE_PCS_SW_RST_MASK;
+
+       writew(val, reg);
+
+       ret = wait_for_bit_le16(&reg, TSE_PCS_SW_RST_MASK, false,
+                               TSE_PCS_SW_RESET_TIMEOUT, false);
+
+       if (ret) {
+               debug("%s: PCS could not get out of sw reset\n", __func__);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+int tse_pcs_init(phys_addr_t base, struct tse_pcs *pcs)
+{
+       int ret = 0;
+
+       writew(TSE_PCS_USE_SGMII_ENA, base + TSE_PCS_IF_MODE_REG);
+       writew(TSE_PCS_SGMII_LINK_TIMER_0, base + TSE_PCS_LINK_TIMER_0_REG);
+       writew(TSE_PCS_SGMII_LINK_TIMER_1, base + TSE_PCS_LINK_TIMER_1_REG);
+
+       ret = tse_pcs_reset(base, pcs);
+       if (ret == 0)
+               writew(SGMII_ADAPTER_ENABLE,
+                      pcs->sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG);
+
+       return ret;
+}
+
+static void pcs_link_timer_callback(struct tse_pcs *pcs)
+{
+       u16 val = 0;
+       phys_addr_t tse_pcs_base = pcs->tse_pcs_base;
+       phys_addr_t sgmii_adapter_base = pcs->sgmii_adapter_base;
+
+       val = readw(tse_pcs_base + TSE_PCS_STATUS_REG);
+       val &= TSE_PCS_STATUS_LINK_MASK;
+
+       if (val != 0) {
+               printf("Adapter: Link is established\n");
+               writew(SGMII_ADAPTER_ENABLE,
+                      sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG);
+       } else {
+               printf("Adapter: Link fail to establish\n");
+       }
+}
+
+static void auto_nego_timer_callback(struct tse_pcs *pcs)
+{
+       u16 val = 0;
+       u16 speed = 0;
+       u16 duplex = 0;
+       phys_addr_t tse_pcs_base = pcs->tse_pcs_base;
+       phys_addr_t sgmii_adapter_base = pcs->sgmii_adapter_base;
+
+       val = readw(tse_pcs_base + TSE_PCS_STATUS_REG);
+       val &= TSE_PCS_STATUS_AN_COMPLETED_MASK;
+
+       if (val != 0) {
+               printf("Adapter: Auto Negotiation is completed\n");
+               val = readw(tse_pcs_base + TSE_PCS_PARTNER_ABILITY_REG);
+               speed = val & TSE_PCS_PARTNER_SPEED_MASK;
+               duplex = val & TSE_PCS_PARTNER_DUPLEX_MASK;
+
+               if (duplex == TSE_PCS_PARTNER_DUPLEX_FULL) {
+                       if (speed == TSE_PCS_PARTNER_SPEED_10)
+                               printf("Adapter: Link Partner is Up - 
10/Full\n");
+                       else if (speed == TSE_PCS_PARTNER_SPEED_100)
+                               printf("Adapter: Link Partner is Up - 
100/Full\n");
+                       else if (speed == TSE_PCS_PARTNER_SPEED_1000)
+                               printf("Adapter: Link Partner is Up - 
1000/Full\n");
+               } else if (duplex == TSE_PCS_PARTNER_DUPLEX_HALF)
+                       printf("Adapter does not support Half Duplex\n");
+               else
+                       printf("Adapter: Invalid Partner Speed and Duplex\n");
+
+               if (duplex == TSE_PCS_PARTNER_DUPLEX_FULL &&
+                   (speed == TSE_PCS_PARTNER_SPEED_10 ||
+                    speed == TSE_PCS_PARTNER_SPEED_100 ||
+                    speed == TSE_PCS_PARTNER_SPEED_1000))
+                       writew(SGMII_ADAPTER_ENABLE,
+                              sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG);
+       } else {
+               printf("Auto-negotioation failed\n");
+       }
+}
+
+void tse_pcs_fix_mac_speed(struct tse_pcs *pcs, struct phy_device *phy_dev,
+                          unsigned int speed)
+{
+       phys_addr_t tse_pcs_base = pcs->tse_pcs_base;
+       phys_addr_t sgmii_adapter_base = pcs->sgmii_adapter_base;
+       u32 val, start;
+
+       writew(SGMII_ADAPTER_ENABLE,
+              sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG);
+       pcs->autoneg = phy_dev->autoneg;
+
+       if (pcs->autoneg == AUTONEG_ENABLE) {
+               val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG);
+               val |= TSE_PCS_CONTROL_AN_EN_MASK;
+               writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG);
+
+               val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG);
+               val |= TSE_PCS_USE_SGMII_AN_MASK;
+               writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG);
+
+               val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG);
+               val |= TSE_PCS_CONTROL_RESTART_AN_MASK;
+
+               tse_pcs_reset(tse_pcs_base, pcs);
+
+       } else if (pcs->autoneg == AUTONEG_DISABLE) {
+               val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG);
+               val &= ~TSE_PCS_CONTROL_AN_EN_MASK;
+               writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG);
+
+               val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG);
+               val &= ~TSE_PCS_USE_SGMII_AN_MASK;
+               writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG);
+
+               val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG);
+               val &= ~TSE_PCS_SGMII_SPEED_MASK;
+
+               switch (speed) {
+               case 1000:
+                       val |= TSE_PCS_SGMII_SPEED_1000;
+                       break;
+               case 100:
+                       val |= TSE_PCS_SGMII_SPEED_100;
+                       break;
+               case 10:
+                       val |= TSE_PCS_SGMII_SPEED_10;
+                       break;
+               default:
+                       return;
+               }
+               writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG);
+
+               tse_pcs_reset(tse_pcs_base, pcs);
+       }
+
+       start = get_timer(0);
+       while (1) {
+               if (get_timer(start) >= AUTONEGO_LINK_TIMER) {
+                       if (pcs->autoneg == AUTONEG_ENABLE)
+                               auto_nego_timer_callback(pcs);
+                       else if (pcs->autoneg == AUTONEG_DISABLE)
+                               pcs_link_timer_callback(pcs);
+                       return;
+               }
+               udelay(100);
+       }
+}
diff --git a/drivers/net/phy/altr_tse_pcs.h b/drivers/net/phy/altr_tse_pcs.h
new file mode 100644
index 0000000..0e8d4f4
--- /dev/null
+++ b/drivers/net/phy/altr_tse_pcs.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Intel Corporation <www.intel.com>
+ */
+
+#ifndef __TSE_PCS_H__
+#define __TSE_PCS_H__
+
+#define TSE_PCS_CONTROL_AN_EN_MASK                     BIT(12)
+#define TSE_PCS_CONTROL_REG                            0x00
+#define TSE_PCS_CONTROL_RESTART_AN_MASK                        BIT(9)
+#define TSE_PCS_IF_MODE_REG                            0x28
+#define TSE_PCS_LINK_TIMER_0_REG                       0x24
+#define TSE_PCS_LINK_TIMER_1_REG                       0x26
+#define TSE_PCS_SIZE                                   0x40
+#define TSE_PCS_STATUS_AN_COMPLETED_MASK               BIT(5)
+#define TSE_PCS_STATUS_LINK_MASK                       BIT(2)
+#define TSE_PCS_STATUS_REG                             0x02
+#define TSE_PCS_SGMII_SPEED_1000                       BIT(3)
+#define TSE_PCS_SGMII_SPEED_100                                BIT(2)
+#define TSE_PCS_SGMII_SPEED_10                         0x0
+#define TSE_PCS_SGMII_SPEED_MASK                       GENMASK(3, 2)
+#define TSE_PCS_SW_RST_MASK                            BIT(15)
+#define TSE_PCS_PARTNER_ABILITY_REG                    0x0A
+#define TSE_PCS_PARTNER_DUPLEX_FULL                    BIT(12)
+#define TSE_PCS_PARTNER_DUPLEX_HALF                    0x0000
+#define TSE_PCS_PARTNER_DUPLEX_MASK                    BIT(12)
+#define TSE_PCS_PARTNER_SPEED_MASK                     GENMASK(11, 10)
+#define TSE_PCS_PARTNER_SPEED_1000                     BIT(11)
+#define TSE_PCS_PARTNER_SPEED_100                      BIT(10)
+#define TSE_PCS_PARTNER_SPEED_10                       0x0000
+#define TSE_PCS_SGMII_LINK_TIMER_0                     0x0D40
+#define TSE_PCS_SGMII_LINK_TIMER_1                     0x0003
+#define TSE_PCS_SW_RESET_TIMEOUT                       100
+#define TSE_PCS_USE_SGMII_AN_MASK                      BIT(1)
+#define TSE_PCS_USE_SGMII_ENA                          BIT(0)
+
+#define SGMII_ADAPTER_CTRL_REG                         0x00
+#define SGMII_ADAPTER_DISABLE                          0x0001
+#define SGMII_ADAPTER_ENABLE                           0x0000
+
+#define AUTONEGO_LINK_TIMER                            20
+
+struct tse_pcs {
+       struct device *dev;
+       phys_addr_t tse_pcs_base;
+       phys_addr_t sgmii_adapter_base;
+       struct timer_list aneg_link_timer;
+       int autoneg;
+};
+
+int tse_pcs_init(phys_addr_t base, struct tse_pcs *pcs);
+void tse_pcs_fix_mac_speed(struct tse_pcs *pcs, struct phy_device *phy_dev,
+                          unsigned int speed);
+
+#endif /* __TSE_PCS_H__ */
-- 
1.7.1

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to