diff mbox series

[U-Boot] net: phy: add TSE PCS support to dwmac-socfpga

Message ID 1537856652-4740-1-git-send-email-joyce.ooi@intel.com
State Superseded
Delegated to: Marek Vasut
Headers show
Series [U-Boot] net: phy: add TSE PCS support to dwmac-socfpga | expand

Commit Message

Joyce Ooi Sept. 25, 2018, 6:24 a.m. UTC
This adds support for TSE PCS that uses SGMII adapter when the
phy-mode in device tree is set to sgmii.

Signed-off-by: Ooi, Joyce <joyce.ooi@intel.com>
---
 drivers/net/Makefile           |   3 +-
 drivers/net/designware.c       |   5 ++
 drivers/net/designware.h       |   1 +
 drivers/net/dwmac_socfpga.c    | 121 +++++++++++++++++++++++++
 drivers/net/phy/altr_tse_pcs.c | 197 +++++++++++++++++++++++++++++++++++++++++
 drivers/net/phy/altr_tse_pcs.h |  59 ++++++++++++
 6 files changed, 385 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/phy/altr_tse_pcs.c
 create mode 100644 drivers/net/phy/altr_tse_pcs.h

Comments

Dinh Nguyen Sept. 25, 2018, 3 p.m. UTC | #1
On 09/25/2018 01:24 AM, Ooi, Joyce wrote:
> This adds support for TSE PCS that uses SGMII adapter when the
> phy-mode in device tree is set to sgmii.

Can you add a bit more description to your commit message? TSE(Triple
Speed Ethernet), but what is PCS?

> 
> Signed-off-by: Ooi, Joyce <joyce.ooi@intel.com>
> ---
>  drivers/net/Makefile           |   3 +-
>  drivers/net/designware.c       |   5 ++
>  drivers/net/designware.h       |   1 +
>  drivers/net/dwmac_socfpga.c    | 121 +++++++++++++++++++++++++
>  drivers/net/phy/altr_tse_pcs.c | 197 +++++++++++++++++++++++++++++++++++++++++
>  drivers/net/phy/altr_tse_pcs.h |  59 ++++++++++++
>  6 files changed, 385 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/net/phy/altr_tse_pcs.c
>  create mode 100644 drivers/net/phy/altr_tse_pcs.h
> 
> 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

Huh? What's this change for?

Also, I'm getting these compile errors against the latest U-Boot, top
commit = "284b27cf81da10d55 Merge branch 'master' of
git://git.denx.de/u-boot-mips"       :

drivers/net/dwmac_socfpga.c: In function ‘socfpga_dw_tse_pcs_init’:
drivers/net/dwmac_socfpga.c:67:9: warning: implicit declaration of
function ‘mbox_get_fpga_config_status’ [-Wimplicit-function-declaration]
   ret = mbox_get_fpga_config_status(MBOX_CONFIG_STATUS);
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
  LD      common/built-in.o
  LD      drivers/net/dwmac-socfpga.o
  LD      drivers/net/built-in.o
  LD      cmd/built-in.o
  CC      lib/smbios.o
  CC      lib/display_options.o
  CC      lib/efi_loader/efi_reloc.o
  CC      lib/efi_loader/helloworld.o
  LD      lib/efi_loader/helloworld_efi.so
  OBJCOPY lib/efi_loader/helloworld.efi
rm lib/efi_loader/helloworld_efi.so lib/efi_loader/helloworld.o
  LD      lib/built-in.o
  LD      u-boot
drivers/net/built-in.o: In function `dwmac_socfpga_ofdata_to_platdata':
/home/dinguyen/linux_dev/u-boot/drivers/net/dwmac_socfpga.c:170:
undefined reference to `syscon_node_to_regmap'
/home/dinguyen/linux_dev/u-boot/drivers/net/dwmac_socfpga.c:177:
undefined reference to `regmap_get_range'
drivers/net/built-in.o: In function `socfpga_dw_tse_pcs_init':
/home/dinguyen/linux_dev/u-boot/drivers/net/dwmac_socfpga.c:67:
undefined reference to `mbox_get_fpga_config_status'


In the future, please test before submitting patches.

Thanks,
Dinh
Dinh Nguyen Sept. 25, 2018, 6:44 p.m. UTC | #2
On 09/25/2018 10:00 AM, Dinh Nguyen wrote:
> 
> 
> On 09/25/2018 01:24 AM, Ooi, Joyce wrote:
>> This adds support for TSE PCS that uses SGMII adapter when the
>> phy-mode in device tree is set to sgmii.
> 
> Can you add a bit more description to your commit message? TSE(Triple
> Speed Ethernet), but what is PCS?
> 
>>
>> Signed-off-by: Ooi, Joyce <joyce.ooi@intel.com>
>> ---
>>  drivers/net/Makefile           |   3 +-
>>  drivers/net/designware.c       |   5 ++
>>  drivers/net/designware.h       |   1 +
>>  drivers/net/dwmac_socfpga.c    | 121 +++++++++++++++++++++++++
>>  drivers/net/phy/altr_tse_pcs.c | 197 +++++++++++++++++++++++++++++++++++++++++
>>  drivers/net/phy/altr_tse_pcs.h |  59 ++++++++++++
>>  6 files changed, 385 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/net/phy/altr_tse_pcs.c
>>  create mode 100644 drivers/net/phy/altr_tse_pcs.h
>>
>> 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
> 
> Huh? What's this change for?
> 
> Also, I'm getting these compile errors against the latest U-Boot, top
> commit = "284b27cf81da10d55 Merge branch 'master' of
> git://git.denx.de/u-boot-mips"       :
> 
> drivers/net/dwmac_socfpga.c: In function ‘socfpga_dw_tse_pcs_init’:
> drivers/net/dwmac_socfpga.c:67:9: warning: implicit declaration of
> function ‘mbox_get_fpga_config_status’ [-Wimplicit-function-declaration]
>    ret = mbox_get_fpga_config_status(MBOX_CONFIG_STATUS);
>          ^~~~~~~~~~~~~~~~~~~~~~~~~~~
>   LD      common/built-in.o
>   LD      drivers/net/dwmac-socfpga.o
>   LD      drivers/net/built-in.o
>   LD      cmd/built-in.o
>   CC      lib/smbios.o
>   CC      lib/display_options.o
>   CC      lib/efi_loader/efi_reloc.o
>   CC      lib/efi_loader/helloworld.o
>   LD      lib/efi_loader/helloworld_efi.so
>   OBJCOPY lib/efi_loader/helloworld.efi
> rm lib/efi_loader/helloworld_efi.so lib/efi_loader/helloworld.o
>   LD      lib/built-in.o
>   LD      u-boot
> drivers/net/built-in.o: In function `dwmac_socfpga_ofdata_to_platdata':
> /home/dinguyen/linux_dev/u-boot/drivers/net/dwmac_socfpga.c:170:
> undefined reference to `syscon_node_to_regmap'
> /home/dinguyen/linux_dev/u-boot/drivers/net/dwmac_socfpga.c:177:
> undefined reference to `regmap_get_range'
> drivers/net/built-in.o: In function `socfpga_dw_tse_pcs_init':
> /home/dinguyen/linux_dev/u-boot/drivers/net/dwmac_socfpga.c:67:
> undefined reference to `mbox_get_fpga_config_status'
> 
> 
> In the future, please test before submitting patches.
> 

For the arria10 defconfig, I get the following compile error:

drivers/net/dwmac_socfpga.c: In function ‘socfpga_dw_tse_pcs_init’:
drivers/net/dwmac_socfpga.c:67:9: warning: implicit declaration of
function ‘mbox_get_fpga_config_status’ [-Wimplicit-function-declaration]
   ret = mbox_get_fpga_config_status(MBOX_CONFIG_STATUS);
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/dwmac_socfpga.c:74:3: error: too many arguments to function
‘socfpga_bridges_reset’
   socfpga_bridges_reset(1);
   ^~~~~~~~~~~~~~~~~~~~~
In file included from ./arch/arm/include/asm/arch/reset_manager.h:44:0,
                 from drivers/net/dwmac_socfpga.c:21:
./arch/arm/include/asm/arch/reset_manager_arria10.h:15:5: note: declared
here
 int socfpga_bridges_reset(void);

Dinh
diff mbox series

Patch

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..a096d29 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/mailbox_s10.h>
+#include <asm/arch/reset_manager.h>
 #include <asm/arch/system_manager.h>
 
 enum dwmac_type {
@@ -27,8 +31,122 @@  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) {
+		/* check FPGA status */
+		ret = mbox_get_fpga_config_status(MBOX_CONFIG_STATUS);
+		if (ret) {
+			debug("%s: FPGA status returns %d\n", __func__, ret);
+			return -EPERM;
+		}
+
+		/* enable HPS bridge */
+		socfpga_bridges_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 +212,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 +241,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..fa8077c
--- /dev/null
+++ b/drivers/net/phy/altr_tse_pcs.c
@@ -0,0 +1,197 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017-2018, Intel Corporation. All rights reserved
+ */
+
+#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 <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 counter = 0;
+	u16 val;
+
+	val = readw(base + TSE_PCS_CONTROL_REG);
+	val |= TSE_PCS_SW_RST_MASK;
+
+	writew(val, base + TSE_PCS_CONTROL_REG);
+
+	while (counter < TSE_PCS_SW_RESET_TIMEOUT) {
+		val = readw(base + TSE_PCS_CONTROL_REG);
+		val &= TSE_PCS_SW_RST_MASK;
+		if (val == 0)
+			break;
+		counter++;
+		udelay(1);
+	}
+
+	if (counter >= TSE_PCS_SW_RESET_TIMEOUT) {
+		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 (speed == TSE_PCS_PARTNER_SPEED_10 &&
+		    duplex == TSE_PCS_PARTNER_DUPLEX_FULL)
+			printf("Adapter: Link Partner is Up - 10/Full\n");
+		else if (speed == TSE_PCS_PARTNER_SPEED_100 &&
+			 duplex == TSE_PCS_PARTNER_DUPLEX_FULL)
+			printf("Adapter: Link Partner is Up - 100/Full\n");
+		else if (speed == TSE_PCS_PARTNER_SPEED_1000 &&
+			 duplex == TSE_PCS_PARTNER_DUPLEX_FULL)
+			printf("Adapter: Link Partner is Up - 1000/Full\n");
+		else if (speed == TSE_PCS_PARTNER_SPEED_10 &&
+			 duplex == TSE_PCS_PARTNER_DUPLEX_HALF)
+			printf("Adapter does not support Half Duplex\n");
+		else if (speed == TSE_PCS_PARTNER_SPEED_100 &&
+			 duplex == TSE_PCS_PARTNER_DUPLEX_HALF)
+			printf("Adapter does not support Half Duplex\n");
+		else if (speed == TSE_PCS_PARTNER_SPEED_1000 &&
+			 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..9a2016a
--- /dev/null
+++ b/drivers/net/phy/altr_tse_pcs.h
@@ -0,0 +1,59 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2017-2018, Intel Corporation. All rights reserved
+ */
+
+#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			0x0004
+#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_SW_RST_MASK				0x8000
+#define TSE_PCS_PARTNER_ABILITY_REG			0x0A
+#define TSE_PCS_PARTNER_DUPLEX_FULL			0x1000
+#define TSE_PCS_PARTNER_DUPLEX_HALF			0x0000
+#define TSE_PCS_PARTNER_DUPLEX_MASK			0x1000
+#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_PARTNER_SPEED_1000			BIT(11)
+#define TSE_PCS_PARTNER_SPEED_100			BIT(10)
+#define TSE_PCS_PARTNER_SPEED_10			0x0000
+#define TSE_PCS_SGMII_SPEED_MASK			GENMASK(3, 2)
+#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__ */