diff mbox series

[net-next,18/20] net: ethernet: qualcomm: Add PPE MAC support for phylink

Message ID 20240110114033.32575-19-quic_luoj@quicinc.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series net: ethernet: Add qcom PPE driver | expand

Checks

Context Check Description
netdev/series_format fail Series longer than 15 patches (and no cover letter)
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 23 this patch: 1082
netdev/cc_maintainers success CCed 0 of 0 maintainers
netdev/build_clang fail Errors and warnings before: 41 this patch: 1116
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 23 this patch: 1109
netdev/checkpatch warning WARNING: line length of 100 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 92 exceeds 80 columns WARNING: line length of 93 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns WARNING: line length of 96 exceeds 80 columns WARNING: line length of 97 exceeds 80 columns WARNING: line length of 98 exceeds 80 columns WARNING: line length of 99 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Jie Luo Jan. 10, 2024, 11:40 a.m. UTC
From: Lei Wei <quic_leiwei@quicinc.com>

This driver adds support for PPE MAC initialization and MAC
operations which used by phylink.

Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
---
 drivers/net/ethernet/qualcomm/Kconfig        |   3 +
 drivers/net/ethernet/qualcomm/ppe/ppe.c      | 904 +++++++++++++++++++
 drivers/net/ethernet/qualcomm/ppe/ppe.h      |  33 +
 drivers/net/ethernet/qualcomm/ppe/ppe_regs.h | 112 +++
 include/linux/soc/qcom/ppe.h                 |  33 +
 5 files changed, 1085 insertions(+)

Comments

Russell King (Oracle) Jan. 10, 2024, 12:18 p.m. UTC | #1
On Wed, Jan 10, 2024 at 07:40:30PM +0800, Luo Jie wrote:
> +static void ppe_phylink_mac_link_up(struct ppe_device *ppe_dev, int port,
> +				    struct phy_device *phy,
> +				    unsigned int mode, phy_interface_t interface,
> +				    int speed, int duplex, bool tx_pause, bool rx_pause)
> +{
> +	struct phylink_pcs *pcs = ppe_phylink_mac_select_pcs(ppe_dev, port, interface);
> +	struct ppe_uniphy *uniphy = pcs_to_ppe_uniphy(pcs);
> +	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
> +
> +	/* Wait uniphy auto-negotiation completion */
> +	ppe_uniphy_autoneg_complete_check(uniphy, port);

Way too late...

> @@ -352,6 +1230,12 @@ static int ppe_port_maxframe_set(struct ppe_device *ppe_dev,
>  }
>  
>  static struct ppe_device_ops qcom_ppe_ops = {
> +	.phylink_setup = ppe_phylink_setup,
> +	.phylink_destroy = ppe_phylink_destroy,
> +	.phylink_mac_config = ppe_phylink_mac_config,
> +	.phylink_mac_link_up = ppe_phylink_mac_link_up,
> +	.phylink_mac_link_down = ppe_phylink_mac_link_down,
> +	.phylink_mac_select_pcs = ppe_phylink_mac_select_pcs,
>  	.set_maxframe = ppe_port_maxframe_set,
>  };

Why this extra layer of abstraction? If you need separate phylink
operations, why not implement separate phylink_mac_ops structures?
Lei Wei Jan. 22, 2024, 3:01 p.m. UTC | #2
On 1/10/2024 8:18 PM, Russell King (Oracle) wrote:
> On Wed, Jan 10, 2024 at 07:40:30PM +0800, Luo Jie wrote:
>> +static void ppe_phylink_mac_link_up(struct ppe_device *ppe_dev, int port,
>> +				    struct phy_device *phy,
>> +				    unsigned int mode, phy_interface_t interface,
>> +				    int speed, int duplex, bool tx_pause, bool rx_pause)
>> +{
>> +	struct phylink_pcs *pcs = ppe_phylink_mac_select_pcs(ppe_dev, port, interface);
>> +	struct ppe_uniphy *uniphy = pcs_to_ppe_uniphy(pcs);
>> +	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
>> +
>> +	/* Wait uniphy auto-negotiation completion */
>> +	ppe_uniphy_autoneg_complete_check(uniphy, port);
> 
> Way too late...
> 


Yes agree, this will be removed. If inband autoneg is used, 
.pcs_get_state should report the link status.  Then this function call 
should not be needed and should be removed.

>> @@ -352,6 +1230,12 @@ static int ppe_port_maxframe_set(struct ppe_device *ppe_dev,
>>   }
>>   
>>   static struct ppe_device_ops qcom_ppe_ops = {
>> +	.phylink_setup = ppe_phylink_setup,
>> +	.phylink_destroy = ppe_phylink_destroy,
>> +	.phylink_mac_config = ppe_phylink_mac_config,
>> +	.phylink_mac_link_up = ppe_phylink_mac_link_up,
>> +	.phylink_mac_link_down = ppe_phylink_mac_link_down,
>> +	.phylink_mac_select_pcs = ppe_phylink_mac_select_pcs,
>>   	.set_maxframe = ppe_port_maxframe_set,
>>   };
> 
> Why this extra layer of abstraction? If you need separate phylink
> operations, why not implement separate phylink_mac_ops structures?
> 

This PPE driver will serve as the base driver for higher level drivers
such as the ethernet DMA (EDMA) driver and the DSA switch driver. The
ppe_device_ops is exported to these higher level drivers, to allow 
access to PPE operations. For example, the EDMA driver (ethernet 
netdevice driver to be pushed for review after the PPE driver) will use 
the phylink_setup/destroy ops for managing netdevice to PHY linkage. The 
set_maxframe op is also to be used by the EDMA driver during MTU change 
operation on the ethernet port.

I also mentioned it in the section "Exported PPE Device Operations" in 
PPE driver documentation:
https://lore.kernel.org/netdev/20240110114033.32575-2-quic_luoj@quicinc.com/

Whereas the PPE DSA switch driver is expected to use the phylink_mac 
ops. However,we will remove the phylink_mac ops from this patch now 
since it is currently unused.
Russell King (Oracle) Jan. 22, 2024, 5:36 p.m. UTC | #3
On Mon, Jan 22, 2024 at 11:01:26PM +0800, Lei Wei wrote:
> 
> 
> On 1/10/2024 8:18 PM, Russell King (Oracle) wrote:
> > On Wed, Jan 10, 2024 at 07:40:30PM +0800, Luo Jie wrote:
> > > @@ -352,6 +1230,12 @@ static int ppe_port_maxframe_set(struct ppe_device *ppe_dev,
> > >   }
> > >   static struct ppe_device_ops qcom_ppe_ops = {
> > > +	.phylink_setup = ppe_phylink_setup,
> > > +	.phylink_destroy = ppe_phylink_destroy,
> > > +	.phylink_mac_config = ppe_phylink_mac_config,
> > > +	.phylink_mac_link_up = ppe_phylink_mac_link_up,
> > > +	.phylink_mac_link_down = ppe_phylink_mac_link_down,
> > > +	.phylink_mac_select_pcs = ppe_phylink_mac_select_pcs,
> > >   	.set_maxframe = ppe_port_maxframe_set,
> > >   };
> > 
> > Why this extra layer of abstraction? If you need separate phylink
> > operations, why not implement separate phylink_mac_ops structures?
> > 
> 
> This PPE driver will serve as the base driver for higher level drivers
> such as the ethernet DMA (EDMA) driver and the DSA switch driver.

Why not have the higher level drivers provide a pointer to the
appropriate phylink_mac_ops structure? Having extra levels of
indirection makes my future maintenance of phylink harder (I'm already
bugged by DSA doing this, and it's a right pain.)

For example, if one of your higher level drivers needs the mac_prepare
or mac_finish functionality, you have to add a shim, extra function
pointers and so on.

If I need to add an extra parameter to a method, then I have to fix
up your shim layer _as well_ as all the called methods - in other
words, it adds extra maintenance burden.

It also makes detecting whether an implementation provides something
or not harder - see the problems when mac_select_pcs() was introduced
and rather than testing to see whether the method is populated, we
have to call the method with a dummy value to discover whether the
sub-driver implements it or not. Honestly, I would really like to get
rid of DSA's phylink_mac_ops shim layer.
diff mbox series

Patch

diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig
index fe826c508f64..261f6b8c0d2e 100644
--- a/drivers/net/ethernet/qualcomm/Kconfig
+++ b/drivers/net/ethernet/qualcomm/Kconfig
@@ -65,6 +65,9 @@  config QCOM_PPE
 	tristate "Qualcomm Technologies, Inc. PPE Ethernet support"
 	depends on HAS_IOMEM && OF
 	depends on COMMON_CLK
+	select PHYLINK
+	select HWMON
+	select SFP
 	help
 	  This driver supports the Qualcomm Technologies, Inc. packet
 	  process engine(PPE) available with IPQ SoC. The PPE houses
diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe.c b/drivers/net/ethernet/qualcomm/ppe/ppe.c
index 21040efe71fc..d241ff3eab84 100644
--- a/drivers/net/ethernet/qualcomm/ppe/ppe.c
+++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c
@@ -13,6 +13,8 @@ 
 #include <linux/regmap.h>
 #include <linux/platform_device.h>
 #include <linux/if_ether.h>
+#include <linux/of_net.h>
+#include <linux/rtnetlink.h>
 #include <linux/soc/qcom/ppe.h>
 #include "ppe.h"
 #include "ppe_regs.h"
@@ -197,6 +199,19 @@  struct reset_control **ppe_reset_get(struct ppe_device *ppe_dev)
 	return ppe_dev_priv->rst;
 }
 
+static struct ppe_port *ppe_port_get(struct ppe_device *ppe_dev, int port)
+{
+	struct ppe_ports *ppe_ports = (struct ppe_ports *)ppe_dev->ports;
+	int i = 0;
+
+	for (i = 0; i < ppe_ports->num; i++) {
+		if (ppe_ports->port[i].port_id == port)
+			return &ppe_ports->port[i];
+	}
+
+	return NULL;
+}
+
 static int ppe_clock_set_enable(struct ppe_device *ppe_dev,
 				enum ppe_clk_id clk_id, unsigned long rate)
 {
@@ -302,6 +317,869 @@  static int ppe_clock_config(struct platform_device *pdev)
 	return 0;
 }
 
+static int ppe_port_mac_reset(struct ppe_device *ppe_dev, int port)
+{
+	struct ppe_data *ppe_dev_priv = ppe_dev->ppe_priv;
+
+	reset_control_assert(ppe_dev_priv->rst[PPE_NSS_PORT1_MAC_RST + port - 1]);
+	if (ppe_dev_priv->ppe_type == PPE_TYPE_APPE) {
+		reset_control_assert(ppe_dev_priv->rst[PPE_NSS_PORT1_RST + port]);
+	} else if (ppe_dev_priv->ppe_type == PPE_TYPE_MPPE) {
+		reset_control_assert(ppe_dev_priv->rst[PPE_NSS_PORT1_RX_RST + ((port - 1) << 1)]);
+		reset_control_assert(ppe_dev_priv->rst[PPE_NSS_PORT1_TX_RST + ((port - 1) << 1)]);
+	}
+	fsleep(150000);
+
+	reset_control_deassert(ppe_dev_priv->rst[PPE_NSS_PORT1_MAC_RST + port - 1]);
+	if (ppe_dev_priv->ppe_type == PPE_TYPE_APPE) {
+		reset_control_deassert(ppe_dev_priv->rst[PPE_NSS_PORT1_RST + port]);
+	} else if (ppe_dev_priv->ppe_type == PPE_TYPE_MPPE) {
+		reset_control_deassert(ppe_dev_priv->rst[PPE_NSS_PORT1_RX_RST + ((port - 1) << 1)]);
+		reset_control_deassert(ppe_dev_priv->rst[PPE_NSS_PORT1_TX_RST + ((port - 1) << 1)]);
+	}
+	fsleep(150000);
+
+	return 0;
+}
+
+static int ppe_gcc_port_speed_clk_set(struct ppe_device *ppe_dev,
+				      int port, int speed, phy_interface_t interface)
+{
+	struct ppe_data *ppe_dev_priv = ppe_dev->ppe_priv;
+	enum ppe_clk_id rx_id, tx_id;
+	unsigned long rate = 0;
+	int err = 0;
+
+	rx_id = PPE_NSS_PORT1_RX_CLK + ((port - 1) << 1);
+	tx_id = PPE_NSS_PORT1_TX_CLK + ((port - 1) << 1);
+
+	switch (interface) {
+	case PHY_INTERFACE_MODE_USXGMII:
+	case PHY_INTERFACE_MODE_10GKR:
+	case PHY_INTERFACE_MODE_QUSGMII:
+	case PHY_INTERFACE_MODE_10GBASER:
+		if (speed == SPEED_10)
+			rate = 1250000;
+		else if (speed == SPEED_100)
+			rate = 12500000;
+		else if (speed == SPEED_1000)
+			rate = 125000000;
+		else if (speed == SPEED_2500)
+			rate = 78125000;
+		else if (speed == SPEED_5000)
+			rate = 156250000;
+		else if (speed == SPEED_10000)
+			rate = 312500000;
+		break;
+	case PHY_INTERFACE_MODE_2500BASEX:
+		if (speed == SPEED_2500)
+			rate = 312500000;
+		break;
+	case PHY_INTERFACE_MODE_QSGMII:
+	case PHY_INTERFACE_MODE_1000BASEX:
+	case PHY_INTERFACE_MODE_SGMII:
+		if (speed == SPEED_10)
+			rate = 2500000;
+		else if (speed == SPEED_100)
+			rate = 25000000;
+		else if (speed == SPEED_1000)
+			rate = 125000000;
+		break;
+	default:
+		break;
+	}
+
+	if (!IS_ERR(ppe_dev_priv->clk[rx_id])) {
+		err = clk_set_rate(ppe_dev_priv->clk[rx_id], rate);
+		if (err) {
+			dev_err(ppe_dev->dev,
+				"Failed to set ppe port %d speed rx clk(%d)\n",
+				port, rx_id);
+			return err;
+		}
+	}
+
+	if (!IS_ERR(ppe_dev_priv->clk[tx_id])) {
+		err = clk_set_rate(ppe_dev_priv->clk[tx_id], rate);
+		if (err) {
+			dev_err(ppe_dev->dev,
+				"Failed to set ppe port %d speed rx clk(%d)\n",
+				port, rx_id);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static int ppe_mac_speed_set(struct ppe_device *ppe_dev,
+			     int port, int speed, phy_interface_t interface)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+	u32 val;
+
+	if (!ppe_port) {
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+		return -ENOENT;
+	}
+
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_SPEED,
+			 &val);
+		val &= ~GMAC_SPEED_MASK;
+		switch (speed) {
+		case SPEED_10:
+			val |= GMAC_SPEED_10;
+			break;
+		case SPEED_100:
+			val |= GMAC_SPEED_100;
+			break;
+		case SPEED_1000:
+			val |= GMAC_SPEED_1000;
+			break;
+		default:
+			break;
+		}
+		ppe_write(ppe_dev,
+			  PPE_PORT_GMAC_ADDR(port) + GMAC_SPEED,
+			  val);
+	} else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+			 &val);
+		val &= ~XGMAC_SPEED_MASK;
+		switch (speed) {
+		case SPEED_10000:
+			if (interface == PHY_INTERFACE_MODE_USXGMII ||
+			    interface == PHY_INTERFACE_MODE_QUSGMII)
+				val |= XGMAC_SPEED_10000_USXGMII;
+			else
+				val |= XGMAC_SPEED_10000;
+			break;
+		case SPEED_5000:
+			val |= XGMAC_SPEED_5000;
+			break;
+		case SPEED_2500:
+			if (interface == PHY_INTERFACE_MODE_USXGMII ||
+			    interface == PHY_INTERFACE_MODE_QUSGMII)
+				val |= XGMAC_SPEED_2500_USXGMII;
+			else
+				val |= XGMAC_SPEED_2500;
+			break;
+		case SPEED_1000:
+			val |= XGMAC_SPEED_1000;
+			break;
+		case SPEED_100:
+			val |= XGMAC_SPEED_100;
+			break;
+		case SPEED_10:
+			val |= XGMAC_SPEED_10;
+			break;
+		default:
+			break;
+		}
+		ppe_write(ppe_dev,
+			  PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+			  val);
+	}
+
+	return 0;
+}
+
+static int ppe_mac_duplex_set(struct ppe_device *ppe_dev, int port, int duplex)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+	u32 val;
+
+	if (!ppe_port) {
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+		return -ENOENT;
+	}
+
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			 &val);
+		if (duplex == DUPLEX_FULL)
+			val |= GMAC_DUPLEX_FULL;
+		else
+			val &= ~GMAC_DUPLEX_FULL;
+		ppe_write(ppe_dev,
+			  PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			  val);
+	}
+
+	return 0;
+}
+
+static int ppe_mac_txfc_status_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+	u32 val;
+
+	if (!ppe_port) {
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+		return -ENOENT;
+	}
+
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			 &val);
+		if (enable)
+			val |= GMAC_TX_FLOW_EN;
+		else
+			val &= ~GMAC_TX_FLOW_EN;
+		ppe_write(ppe_dev,
+			  PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			  val);
+	} else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_Q0_TX_FLOW_CTRL,
+			 &val);
+		if (enable) {
+			val &= ~XGMAC_PT_MASK;
+			val |= (XGMAC_PAUSE_TIME | XGMAC_TFE);
+		} else {
+			val &= ~XGMAC_TFE;
+		}
+		ppe_write(ppe_dev,
+			  PPE_PORT_XGMAC_ADDR(port) + XGMAC_Q0_TX_FLOW_CTRL,
+			  val);
+	}
+
+	ppe_read(ppe_dev,
+		 PPE_BM_PORT_FC_MODE + PPE_BM_PORT_FC_MODE_INC * (port + 7),
+		 &val);
+	if (enable)
+		val |= PPE_BM_PORT_FC_MODE_EN;
+	else
+		val &= ~PPE_BM_PORT_FC_MODE_EN;
+	ppe_write(ppe_dev,
+		  PPE_BM_PORT_FC_MODE + PPE_BM_PORT_FC_MODE_INC * (port + 7),
+		  val);
+
+	return 0;
+}
+
+static int ppe_mac_rxfc_status_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+	u32 val;
+
+	if (!ppe_port) {
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+		return -ENOENT;
+	}
+
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			 &val);
+		if (enable)
+			val |= GMAC_RX_FLOW_EN;
+		else
+			val &= ~GMAC_RX_FLOW_EN;
+		ppe_write(ppe_dev,
+			  PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			  val);
+	} else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_FLOW_CTRL,
+			 &val);
+		if (enable)
+			val |= XGMAC_RFE;
+		else
+			val &= ~XGMAC_RFE;
+		ppe_write(ppe_dev,
+			  PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_FLOW_CTRL,
+			  val);
+	}
+
+	return 0;
+}
+
+static int ppe_mac_txmac_en_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+	u32 val;
+
+	if (!ppe_port) {
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+		return -ENOENT;
+	}
+
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			 &val);
+		if (enable)
+			val |= GMAC_TXMAC_EN;
+		else
+			val &= ~GMAC_TXMAC_EN;
+		ppe_write(ppe_dev,
+			  PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			  val);
+	} else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+			 &val);
+		if (enable)
+			val |= XGMAC_TE;
+		else
+			val &= ~XGMAC_TE;
+		ppe_write(ppe_dev,
+			  PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+			  val);
+	}
+
+	return 0;
+}
+
+static int ppe_mac_rxmac_en_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+	u32 val;
+
+	if (!ppe_port) {
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+		return -ENOENT;
+	}
+
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			 &val);
+		if (enable)
+			val |= GMAC_RXMAC_EN;
+		else
+			val &= ~GMAC_RXMAC_EN;
+		ppe_write(ppe_dev,
+			  PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			  val);
+	} else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+		ppe_read(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_CONFIGURATION,
+			 &val);
+		if (enable)
+			val |= XGMAC_RE;
+		else
+			val &= ~XGMAC_RE;
+		ppe_write(ppe_dev,
+			  PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_CONFIGURATION,
+			  val);
+	}
+
+	return 0;
+}
+
+static int ppe_port_bridge_txmac_en_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+	u32 val;
+
+	ppe_read(ppe_dev,
+		 PPE_PORT_BRIDGE_CTRL + PPE_PORT_BRIDGE_CTRL_INC * port,
+		 &val);
+
+	if (enable)
+		val |= PPE_PORT_BRIDGE_CTRL_TXMAC_EN;
+	else
+		val &= ~PPE_PORT_BRIDGE_CTRL_TXMAC_EN;
+
+	ppe_write(ppe_dev,
+		  PPE_PORT_BRIDGE_CTRL + PPE_PORT_BRIDGE_CTRL_INC * port,
+		  val);
+
+	return 0;
+}
+
+static void ppe_phylink_mac_config(struct ppe_device *ppe_dev, int port,
+				   unsigned int mode, const struct phylink_link_state *state)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+	int mac_type;
+	u32 val;
+
+	if (!ppe_port) {
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+		return;
+	}
+
+	switch (state->interface) {
+	case PHY_INTERFACE_MODE_USXGMII:
+	case PHY_INTERFACE_MODE_2500BASEX:
+	case PHY_INTERFACE_MODE_10GBASER:
+	case PHY_INTERFACE_MODE_QUSGMII:
+		mac_type = PPE_MAC_TYPE_XGMAC;
+		break;
+	default:
+		mac_type = PPE_MAC_TYPE_GMAC;
+		break;
+	}
+
+	if (ppe_port->mac_type != mac_type) {
+		/* Reset port mac for gmac */
+		if (mac_type == PPE_MAC_TYPE_GMAC)
+			ppe_port_mac_reset(ppe_dev, port);
+
+		/* Port mux to select gmac or xgmac */
+		mutex_lock(&ppe_dev->reg_mutex);
+		ppe_read(ppe_dev, PPE_PORT_MUX_CTRL, &val);
+		if (mac_type == PPE_MAC_TYPE_GMAC)
+			val &= ~PPE_PORT_MAC_SEL(port);
+		else
+			val |= PPE_PORT_MAC_SEL(port);
+		if (port == PPE_PORT5)
+			val |= PPE_PORT5_PCS_SEL;
+
+		ppe_write(ppe_dev, PPE_PORT_MUX_CTRL, val);
+		mutex_unlock(&ppe_dev->reg_mutex);
+		ppe_port->mac_type = mac_type;
+	}
+
+	/* Reset ppe port link status when interface changes,
+	 * this allows PPE MAC and UNIPHY to be configured
+	 * according to the port link up status in ppe phylink
+	 * mac link up.
+	 */
+	if (state->interface != ppe_port->interface) {
+		ppe_port->speed = SPEED_UNKNOWN;
+		ppe_port->duplex = DUPLEX_UNKNOWN;
+		ppe_port->pause = MLO_PAUSE_NONE;
+		ppe_port->interface = state->interface;
+	}
+
+	dev_info(ppe_dev->dev, "PPE port %d mac config: interface %s, mac_type %d\n",
+		 port, phy_modes(state->interface), mac_type);
+}
+
+static struct phylink_pcs *ppe_phylink_mac_select_pcs(struct ppe_device *ppe_dev,
+						      int port, phy_interface_t interface)
+{
+	struct ppe_uniphy *uniphy = (struct ppe_uniphy *)ppe_dev->uniphy;
+	int ppe_type = ppe_type_get(ppe_dev);
+	int index;
+
+	switch (port) {
+	case PPE_PORT6:
+		index = 2;
+		break;
+	case PPE_PORT5:
+		index = 1;
+		break;
+	case PPE_PORT4:
+	case PPE_PORT3:
+		index = 0;
+		break;
+	case PPE_PORT2:
+		if (ppe_type == PPE_TYPE_MPPE)
+			index = 1;
+		else if (ppe_type == PPE_TYPE_APPE)
+			index = 0;
+		break;
+	case PPE_PORT1:
+		index = 0;
+		break;
+	default:
+		index = -1;
+		break;
+	}
+
+	if (index >= 0)
+		return &uniphy[index].pcs;
+	else
+		return NULL;
+}
+
+static void ppe_phylink_mac_link_up(struct ppe_device *ppe_dev, int port,
+				    struct phy_device *phy,
+				    unsigned int mode, phy_interface_t interface,
+				    int speed, int duplex, bool tx_pause, bool rx_pause)
+{
+	struct phylink_pcs *pcs = ppe_phylink_mac_select_pcs(ppe_dev, port, interface);
+	struct ppe_uniphy *uniphy = pcs_to_ppe_uniphy(pcs);
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+
+	/* Wait uniphy auto-negotiation completion */
+	ppe_uniphy_autoneg_complete_check(uniphy, port);
+
+	if (speed != ppe_port->speed ||
+	    duplex != ppe_port->duplex ||
+		tx_pause != !!(ppe_port->pause & MLO_PAUSE_TX) ||
+		rx_pause != !!(ppe_port->pause & MLO_PAUSE_RX)) {
+		/* Disable gcc uniphy port clk */
+		ppe_uniphy_port_gcc_clock_en_set(uniphy, port, false);
+
+		if (speed != ppe_port->speed) {
+			/* Set gcc port speed clock */
+			ppe_gcc_port_speed_clk_set(ppe_dev, port, speed, interface);
+			fsleep(10000);
+			/* Set uniphy channel speed */
+			ppe_uniphy_speed_set(uniphy, port, speed);
+			/* Set mac speed */
+			ppe_mac_speed_set(ppe_dev, port, speed, interface);
+			ppe_port->speed = speed;
+		}
+
+		if (duplex != ppe_port->duplex) {
+			/* Set uniphy channel duplex */
+			ppe_uniphy_duplex_set(uniphy, port, duplex);
+			/* Set mac duplex */
+			ppe_mac_duplex_set(ppe_dev, port, duplex);
+			ppe_port->duplex = duplex;
+		}
+
+		if (tx_pause != !!(ppe_port->pause & MLO_PAUSE_TX)) {
+			/* Set mac tx flow ctrl */
+			ppe_mac_txfc_status_set(ppe_dev, port, tx_pause);
+			if (tx_pause)
+				ppe_port->pause |= MLO_PAUSE_TX;
+			else
+				ppe_port->pause &= ~MLO_PAUSE_TX;
+		}
+
+		if (rx_pause != !!(ppe_port->pause & MLO_PAUSE_RX)) {
+			/* Set mac rx flow ctrl */
+			ppe_mac_rxfc_status_set(ppe_dev, port, rx_pause);
+			if (rx_pause)
+				ppe_port->pause |= MLO_PAUSE_RX;
+			else
+				ppe_port->pause &= ~MLO_PAUSE_RX;
+		}
+
+		/* Enable gcc uniphy port clk */
+		ppe_uniphy_port_gcc_clock_en_set(uniphy, port, true);
+
+		/* Reset uniphy channel adapter */
+		ppe_uniphy_adapter_reset(uniphy, port);
+	}
+
+	/* Enable ppe mac tx and rx */
+	ppe_mac_txmac_en_set(ppe_dev, port, true);
+	ppe_mac_rxmac_en_set(ppe_dev, port, true);
+
+	/* Enable ppe bridge port tx mac */
+	ppe_port_bridge_txmac_en_set(ppe_dev, port, true);
+
+	dev_info(ppe_dev->dev,
+		 "PPE port %d interface %s link up - %s%s - pause tx %d rx %d\n",
+		 port, phy_modes(interface), phy_speed_to_str(speed),
+		 phy_duplex_to_str(duplex), tx_pause, rx_pause);
+}
+
+static void ppe_phylink_mac_link_down(struct ppe_device *ppe_dev, int port,
+				      unsigned int mode, phy_interface_t interface)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+
+	if (!ppe_port)
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+
+	/* Disable ppe port bridge tx mac */
+	ppe_port_bridge_txmac_en_set(ppe_dev, port, false);
+
+	/* Disable ppe mac rx */
+	ppe_mac_rxmac_en_set(ppe_dev, port, false);
+	fsleep(10000);
+
+	/* Disable ppe mac tx */
+	ppe_mac_txmac_en_set(ppe_dev, port, false);
+
+	dev_info(ppe_dev->dev, "PPE port %d interface %s link down\n",
+		 port, phy_modes(interface));
+}
+
+static int ppe_mac_init(struct platform_device *pdev)
+{
+	struct device_node *ports_node, *port_node;
+	struct ppe_device *ppe_dev = platform_get_drvdata(pdev);
+	struct ppe_ports *ppe_ports = NULL;
+	phy_interface_t phy_mode = PHY_INTERFACE_MODE_NA;
+	int i = 0, port = 0, err = 0, port_num = 0;
+
+	ports_node = of_get_child_by_name(pdev->dev.of_node, "qcom,port_phyinfo");
+	if (!ports_node) {
+		dev_err(&pdev->dev, "Failed to get qcom port phy info node\n");
+		return -ENODEV;
+	}
+
+	port_num = of_get_child_count(ports_node);
+
+	ppe_ports = devm_kzalloc(&pdev->dev,
+				 struct_size(ppe_ports, port, port_num),
+				 GFP_KERNEL);
+	if (!ppe_ports) {
+		err = -ENOMEM;
+		goto err_ports_node_put;
+	}
+
+	ppe_dev->ports = ppe_ports;
+	ppe_ports->num = port_num;
+
+	for_each_available_child_of_node(ports_node, port_node) {
+		err = of_property_read_u32(port_node, "port_id", &port);
+		if (err) {
+			dev_err(&pdev->dev, "Failed to get port id\n");
+			goto err_port_node_put;
+		}
+
+		err = of_get_phy_mode(port_node, &phy_mode);
+		if (err) {
+			dev_err(&pdev->dev, "Failed to get phy mode\n");
+			goto err_port_node_put;
+		}
+
+		ppe_ports->port[i].ppe_dev = ppe_dev;
+		ppe_ports->port[i].port_id = port;
+		ppe_ports->port[i].np = port_node;
+		ppe_ports->port[i].interface = phy_mode;
+		ppe_ports->port[i].mac_type = PPE_MAC_TYPE_NA;
+		ppe_ports->port[i].speed = SPEED_UNKNOWN;
+		ppe_ports->port[i].duplex = DUPLEX_UNKNOWN;
+		ppe_ports->port[i].pause = MLO_PAUSE_NONE;
+		i++;
+
+		/* Port gmac HW initialization */
+		ppe_mask(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+			 GMAC_MAC_EN, 0);
+
+		ppe_mask(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_JUMBO_SIZE,
+			 GMAC_JUMBO_SIZE_MASK,
+			 FIELD_PREP(GMAC_JUMBO_SIZE_MASK, MAC_MAX_FRAME_SIZE));
+
+		ppe_mask(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_CTRL2,
+			 GMAC_INIT_CTRL2_FIELD, GMAC_INIT_CTRL2);
+
+		ppe_mask(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_DBG_CTRL,
+			 GMAC_HIGH_IPG_MASK,
+			 FIELD_PREP(GMAC_HIGH_IPG_MASK, GMAC_IPG_CHECK));
+
+		ppe_mask(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_MIB_CTRL,
+			 MAC_MIB_EN | MAC_MIB_RD_CLR | MAC_MIB_RESET,
+			 MAC_MIB_EN | MAC_MIB_RD_CLR | MAC_MIB_RESET);
+
+		ppe_mask(ppe_dev,
+			 PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_MIB_CTRL,
+			 MAC_MIB_RESET, 0);
+
+		/* Port xgmac HW initialization */
+		ppe_mask(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+			 XGMAC_INIT_TX_CONFIG_FIELD, XGMAC_INIT_TX_CONFIG);
+
+		ppe_mask(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_CONFIGURATION,
+			 XGMAC_INIT_RX_CONFIG_FIELD, XGMAC_INIT_RX_CONFIG);
+
+		ppe_mask(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_WATCHDOG_TIMEOUT,
+			 XGMAC_INIT_WATCHDOG_FIELD, XGMAC_INIT_WATCHDOG);
+
+		ppe_mask(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_PACKET_FILTER,
+			 XGMAC_INIT_FILTER_FIELD, XGMAC_INIT_FILTER);
+
+		ppe_mask(ppe_dev,
+			 PPE_PORT_XGMAC_ADDR(port) + XGMAC_MMC_CONTROL,
+			 XGMAC_MCF | XGMAC_CNTRST, XGMAC_CNTRST);
+	}
+
+	of_node_put(ports_node);
+	dev_info(ppe_dev->dev, "QCOM PPE MAC init success\n");
+	return 0;
+
+err_port_node_put:
+	of_node_put(port_node);
+err_ports_node_put:
+	of_node_put(ports_node);
+	return err;
+}
+
+static void ppe_mac_config(struct phylink_config *config, unsigned int mode,
+			   const struct phylink_link_state *state)
+{
+	struct ppe_device *ppe_dev = NULL;
+	struct ppe_port *ppe_port = container_of(config,
+						 struct ppe_port,
+						 phylink_config);
+
+	if (!ppe_port)
+		dev_err(ppe_dev->dev, "Failed to find ppe port\n");
+
+	ppe_dev = ppe_port->ppe_dev;
+
+	if (ppe_dev && ppe_dev->ppe_ops &&
+	    ppe_dev->ppe_ops->phylink_mac_config) {
+		ppe_dev->ppe_ops->phylink_mac_config(ppe_dev,
+						     ppe_port->port_id,
+						     mode, state);
+	} else {
+		dev_err(ppe_dev->dev,
+			"Failed to find ppe device mac config operation\n");
+	}
+}
+
+static void ppe_mac_link_down(struct phylink_config *config, unsigned int mode,
+			      phy_interface_t interface)
+{
+	struct ppe_device *ppe_dev = NULL;
+	struct ppe_port *ppe_port = container_of(config,
+						 struct ppe_port,
+						 phylink_config);
+
+	if (!ppe_port)
+		dev_err(ppe_dev->dev, "Failed to find ppe port\n");
+
+	ppe_dev = ppe_port->ppe_dev;
+
+	if (ppe_dev && ppe_dev->ppe_ops &&
+	    ppe_dev->ppe_ops->phylink_mac_link_down) {
+		ppe_dev->ppe_ops->phylink_mac_link_down(ppe_dev,
+							ppe_port->port_id,
+							mode, interface);
+	} else {
+		dev_err(ppe_dev->dev,
+			"Failed to find ppe device link down operation\n");
+	}
+}
+
+static void ppe_mac_link_up(struct phylink_config *config,
+			    struct phy_device *phy,
+			    unsigned int mode, phy_interface_t interface,
+			    int speed, int duplex, bool tx_pause, bool rx_pause)
+{
+	struct ppe_device *ppe_dev = NULL;
+	struct ppe_port *ppe_port = container_of(config,
+						 struct ppe_port,
+						 phylink_config);
+
+	if (!ppe_port)
+		dev_err(ppe_dev->dev, "Failed to find ppe port\n");
+
+	ppe_dev = ppe_port->ppe_dev;
+
+	if (ppe_dev && ppe_dev->ppe_ops &&
+	    ppe_dev->ppe_ops->phylink_mac_link_up) {
+		ppe_dev->ppe_ops->phylink_mac_link_up(ppe_dev,
+						      ppe_port->port_id,
+						      phy, mode, interface,
+						      speed, duplex,
+						      tx_pause, rx_pause);
+	} else {
+		dev_err(ppe_dev->dev,
+			"Failed to find ppe device link up operation\n");
+	}
+}
+
+static struct phylink_pcs *ppe_mac_select_pcs(struct phylink_config *config,
+					      phy_interface_t interface)
+{
+	struct ppe_device *ppe_dev = NULL;
+	struct ppe_port *ppe_port = container_of(config,
+						 struct ppe_port,
+						 phylink_config);
+
+	if (!ppe_port) {
+		dev_err(ppe_dev->dev, "Failed to find ppe port");
+		return NULL;
+	}
+
+	ppe_dev = ppe_port->ppe_dev;
+
+	if (ppe_dev && ppe_dev->ppe_ops &&
+	    ppe_dev->ppe_ops->phylink_mac_select_pcs) {
+		return ppe_dev->ppe_ops->phylink_mac_select_pcs(ppe_dev,
+								ppe_port->port_id,
+								interface);
+	} else {
+		dev_err(ppe_dev->dev,
+			"Failed to find ppe device pcs select operation\n");
+		return NULL;
+	}
+}
+
+static const struct phylink_mac_ops ppe_phylink_ops = {
+	.mac_config = ppe_mac_config,
+	.mac_link_down = ppe_mac_link_down,
+	.mac_link_up = ppe_mac_link_up,
+	.mac_select_pcs = ppe_mac_select_pcs,
+};
+
+static struct phylink *ppe_phylink_setup(struct ppe_device *ppe_dev,
+					 struct net_device *netdev,
+					 int port)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+	int err;
+
+	if (!ppe_port) {
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+		return NULL;
+	}
+
+	/* per port phylink capability */
+	ppe_port->phylink_config.dev = &netdev->dev;
+	ppe_port->phylink_config.type = PHYLINK_NETDEV;
+	ppe_port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+		MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD | MAC_5000FD | MAC_10000FD;
+	__set_bit(PHY_INTERFACE_MODE_SGMII,
+		  ppe_port->phylink_config.supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_1000BASEX,
+		  ppe_port->phylink_config.supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_2500BASEX,
+		  ppe_port->phylink_config.supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_USXGMII,
+		  ppe_port->phylink_config.supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_10GBASER,
+		  ppe_port->phylink_config.supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_QSGMII,
+		  ppe_port->phylink_config.supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_QUSGMII,
+		  ppe_port->phylink_config.supported_interfaces);
+
+	/* create phylink */
+	ppe_port->phylink = phylink_create(&ppe_port->phylink_config,
+					   of_fwnode_handle(ppe_port->np),
+					   ppe_port->interface, &ppe_phylink_ops);
+	if (IS_ERR(ppe_port->phylink)) {
+		dev_err(ppe_dev->dev, "Failed to create phylink for port %d\n", port);
+		return NULL;
+	}
+
+	/* connect phylink */
+	err = phylink_of_phy_connect(ppe_port->phylink, ppe_port->np, 0);
+	if (err) {
+		dev_err(ppe_dev->dev, "Failed to connect phylink for port %d\n", port);
+		phylink_destroy(ppe_port->phylink);
+		ppe_port->phylink = NULL;
+		return NULL;
+	}
+
+	return ppe_port->phylink;
+}
+
+static void ppe_phylink_destroy(struct ppe_device *ppe_dev, int port)
+{
+	struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+
+	if (!ppe_port)
+		dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+
+	if (ppe_port->phylink) {
+		rtnl_lock();
+		phylink_disconnect_phy(ppe_port->phylink);
+		rtnl_unlock();
+		phylink_destroy(ppe_port->phylink);
+		ppe_port->phylink = NULL;
+	}
+}
+
 bool ppe_is_probed(struct platform_device *pdev)
 {
 	struct ppe_device *ppe_dev = platform_get_drvdata(pdev);
@@ -352,6 +1230,12 @@  static int ppe_port_maxframe_set(struct ppe_device *ppe_dev,
 }
 
 static struct ppe_device_ops qcom_ppe_ops = {
+	.phylink_setup = ppe_phylink_setup,
+	.phylink_destroy = ppe_phylink_destroy,
+	.phylink_mac_config = ppe_phylink_mac_config,
+	.phylink_mac_link_up = ppe_phylink_mac_link_up,
+	.phylink_mac_link_down = ppe_phylink_mac_link_down,
+	.phylink_mac_select_pcs = ppe_phylink_mac_select_pcs,
 	.set_maxframe = ppe_port_maxframe_set,
 };
 
@@ -1407,6 +2291,7 @@  static int qcom_ppe_probe(struct platform_device *pdev)
 				     PTR_ERR(ppe_dev->ppe_priv),
 				     "Fail to init ppe data\n");
 
+	mutex_init(&ppe_dev->reg_mutex);
 	platform_set_drvdata(pdev, ppe_dev);
 	ret = ppe_clock_config(pdev);
 	if (ret)
@@ -1426,6 +2311,10 @@  static int qcom_ppe_probe(struct platform_device *pdev)
 				     ret,
 				     "ppe device hw init failed\n");
 
+	ret = ppe_mac_init(pdev);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "ppe mac initialization failed\n");
+
 	ppe_dev->uniphy = ppe_uniphy_setup(pdev);
 	if (IS_ERR(ppe_dev->uniphy))
 		return dev_err_probe(&pdev->dev, ret, "ppe uniphy initialization failed\n");
@@ -1440,10 +2329,25 @@  static int qcom_ppe_probe(struct platform_device *pdev)
 static int qcom_ppe_remove(struct platform_device *pdev)
 {
 	struct ppe_device *ppe_dev;
+	struct ppe_ports *ppe_ports;
+	struct ppe_data *ppe_dev_priv;
+	int i, port;
 
 	ppe_dev = platform_get_drvdata(pdev);
+	ppe_dev_priv = ppe_dev->ppe_priv;
+	ppe_ports = (struct ppe_ports *)ppe_dev->ports;
+
 	ppe_debugfs_teardown(ppe_dev);
 
+	for (i = 0; i < ppe_ports->num; i++) {
+		/* Reset ppe port parent clock to XO clock */
+		port = ppe_ports->port[i].port_id;
+		clk_set_rate(ppe_dev_priv->clk[PPE_NSS_PORT1_RX_CLK + ((port - 1) << 1)],
+			     P_XO_CLOCK_RATE);
+		clk_set_rate(ppe_dev_priv->clk[PPE_NSS_PORT1_TX_CLK + ((port - 1) << 1)],
+			     P_XO_CLOCK_RATE);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe.h b/drivers/net/ethernet/qualcomm/ppe/ppe.h
index 45b70f47cd21..532d53c05bf9 100644
--- a/drivers/net/ethernet/qualcomm/ppe/ppe.h
+++ b/drivers/net/ethernet/qualcomm/ppe/ppe.h
@@ -21,6 +21,9 @@ 
 #define PPE_PORT6		6
 #define PPE_PORT7		7
 
+/* PPE Port XO Clock Rate */
+#define P_XO_CLOCK_RATE		24000000
+
 enum ppe_clk_id {
 	/* clocks for CMN PLL */
 	PPE_CMN_AHB_CLK,
@@ -152,6 +155,14 @@  enum {
 	PPE_ACTION_REDIRECTED_TO_CPU
 };
 
+/* PPE MAC Type */
+enum {
+	PPE_MAC_TYPE_NA,
+	PPE_MAC_TYPE_GMAC,
+	PPE_MAC_TYPE_XGMAC,
+	PPE_MAC_TYPE_MAX
+};
+
 /* PPE private data of different PPE type device */
 struct ppe_data {
 	int ppe_type;
@@ -172,6 +183,28 @@  struct ppe_scheduler_port_resource {
 	int l1edrr[2];
 };
 
+/* PPE per port data type to record port settings such as phylink
+ * setting, mac type, interface mode and link speed.
+ */
+struct ppe_port {
+	struct phylink *phylink;
+	struct phylink_config phylink_config;
+	struct device_node *np;
+	struct ppe_device *ppe_dev;
+	phy_interface_t interface;
+	int mac_type;
+	int port_id;
+	int speed;
+	int duplex;
+	int pause;
+};
+
+/* PPE ports data type */
+struct ppe_ports {
+	unsigned int num;
+	struct ppe_port port[];
+};
+
 int ppe_type_get(struct ppe_device *ppe_dev);
 struct clk **ppe_clock_get(struct ppe_device *ppe_dev);
 struct reset_control **ppe_reset_get(struct ppe_device *ppe_dev);
diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
index 13115405bad9..43cd067c8c73 100644
--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
@@ -7,6 +7,16 @@ 
 #ifndef __PPE_REGS_H__
 #define __PPE_REGS_H__
 
+#define PPE_PORT_MUX_CTRL					0x10
+#define PPE_PORT6_MAC_SEL					BIT(13)
+#define PPE_PORT5_MAC_SEL					BIT(12)
+#define PPE_PORT4_MAC_SEL					BIT(11)
+#define PPE_PORT3_MAC_SEL					BIT(10)
+#define PPE_PORT2_MAC_SEL					BIT(9)
+#define PPE_PORT1_MAC_SEL					BIT(8)
+#define PPE_PORT5_PCS_SEL					BIT(4)
+#define PPE_PORT_MAC_SEL(x)					(PPE_PORT1_MAC_SEL << ((x) - 1))
+
 #define PPE_BM_TDM_CTRL						0xb000
 #define PPE_BM_TDM_CTRL_NUM					1
 #define PPE_BM_TDM_CTRL_INC					4
@@ -819,4 +829,106 @@  union ppe_ac_grp_cfg_u {
 #define PPE_ENQ_OPR_TBL_INC					0x10
 #define PPE_ENQ_OPR_TBL_ENQ_DISABLE				BIT(0)
 
+/* PPE MAC Address */
+#define PPE_PORT_GMAC_ADDR(x)					(0x001000 + ((x) - 1) * 0x200)
+#define PPE_PORT_XGMAC_ADDR(x)					(0x500000 + ((x) - 1) * 0x4000)
+
+/* GMAC Registers */
+#define GMAC_ENABLE						0x0
+#define GMAC_TX_FLOW_EN						BIT(6)
+#define GMAC_RX_FLOW_EN						BIT(5)
+#define GMAC_DUPLEX_FULL					BIT(4)
+#define GMAC_TXMAC_EN						BIT(1)
+#define GMAC_RXMAC_EN						BIT(0)
+#define GMAC_MAC_EN						(GMAC_RXMAC_EN | GMAC_TXMAC_EN)
+
+#define GMAC_SPEED						0x4
+#define GMAC_SPEED_MASK						GENMASK(1, 0)
+#define GMAC_SPEED_10						0
+#define GMAC_SPEED_100						1
+#define GMAC_SPEED_1000						2
+
+#define GMAC_MAC_CTRL2						0x18
+#define GMAC_TX_THD_MASK					GENMASK(27, 24)
+#define GMAC_MAXFR_MASK						GENMASK(21, 8)
+#define GMAC_CRS_SEL						BIT(6)
+#define GMAC_TX_THD						0x1
+#define GMAC_INIT_CTRL2_FIELD					(GMAC_MAXFR_MASK | \
+								GMAC_CRS_SEL | GMAC_TX_THD_MASK)
+#define GMAC_INIT_CTRL2						(FIELD_PREP(GMAC_MAXFR_MASK, \
+				MAC_MAX_FRAME_SIZE) | FIELD_PREP(GMAC_TX_THD_MASK, GMAC_TX_THD))
+
+#define GMAC_MAC_DBG_CTRL					0x1c
+#define GMAC_HIGH_IPG_MASK					GENMASK(15, 8)
+#define GMAC_IPG_CHECK						0xc
+
+#define GMAC_MAC_JUMBO_SIZE					0x30
+#define GMAC_JUMBO_SIZE_MASK					GENMASK(13, 0)
+#define MAC_MAX_FRAME_SIZE					0x3000
+
+#define GMAC_MAC_MIB_CTRL					0x34
+#define MAC_MIB_RD_CLR						BIT(2)
+#define MAC_MIB_RESET						BIT(1)
+#define MAC_MIB_EN						BIT(0)
+
+/* XGMAC Registers */
+#define XGMAC_TX_CONFIGURATION					0x0
+#define XGMAC_SPEED_MASK					GENMASK(31, 29)
+#define XGMAC_SPEED_10000_USXGMII				FIELD_PREP(XGMAC_SPEED_MASK, 4)
+#define XGMAC_SPEED_10000					FIELD_PREP(XGMAC_SPEED_MASK, 0)
+#define XGMAC_SPEED_5000					FIELD_PREP(XGMAC_SPEED_MASK, 5)
+#define XGMAC_SPEED_2500_USXGMII				FIELD_PREP(XGMAC_SPEED_MASK, 6)
+#define XGMAC_SPEED_2500					FIELD_PREP(XGMAC_SPEED_MASK, 2)
+#define XGMAC_SPEED_1000					FIELD_PREP(XGMAC_SPEED_MASK, 3)
+#define XGMAC_SPEED_100						XGMAC_SPEED_1000
+#define XGMAC_SPEED_10						XGMAC_SPEED_1000
+
+#define XGMAC_JD						BIT(16)
+#define XGMAC_TE						BIT(0)
+#define XGMAC_INIT_TX_CONFIG_FIELD				(XGMAC_JD | XGMAC_TE)
+#define XGMAC_INIT_TX_CONFIG					XGMAC_JD
+
+#define XGMAC_RX_CONFIGURATION					0x4
+#define XGMAC_GPSL_MASK						GENMASK(29, 16)
+#define XGMAC_WD						BIT(7)
+#define XGMAC_GPSLCE						BIT(6)
+#define XGMAC_CST						BIT(2)
+#define XGMAC_ACS						BIT(1)
+#define XGMAC_RE						BIT(0)
+#define XGMAC_INIT_RX_CONFIG_FIELD				(XGMAC_RE | XGMAC_ACS | \
+					XGMAC_CST | XGMAC_WD | XGMAC_GPSLCE | XGMAC_GPSL_MASK)
+#define XGMAC_INIT_RX_CONFIG					(XGMAC_ACS | XGMAC_CST | \
+				XGMAC_GPSLCE | FIELD_PREP(XGMAC_GPSL_MASK, MAC_MAX_FRAME_SIZE))
+
+#define XGMAC_PACKET_FILTER					0x8
+#define XGMAC_RA						BIT(31)
+#define XGMAC_PCF_MASK						GENMASK(7, 6)
+#define XGMAC_PR						BIT(0)
+#define XGMAC_PASS_CONTROL_PACKET				0x2
+#define XGMAC_INIT_FILTER_FIELD					(XGMAC_RA | XGMAC_PR | \
+									XGMAC_PCF_MASK)
+#define XGMAC_INIT_FILTER					(XGMAC_RA | XGMAC_PR | \
+								FIELD_PREP(XGMAC_PCF_MASK, \
+									XGMAC_PASS_CONTROL_PACKET))
+
+#define XGMAC_WATCHDOG_TIMEOUT					0xc
+#define XGMAC_PWE						BIT(8)
+#define XGMAC_WTO_MASK						GENMASK(3, 0)
+#define XGMAC_WTO_LIMIT_13K					0xb
+#define XGMAC_INIT_WATCHDOG_FIELD				(XGMAC_PWE | XGMAC_WTO_MASK)
+#define XGMAC_INIT_WATCHDOG					(XGMAC_PWE | \
+						FIELD_PREP(XGMAC_WTO_MASK, XGMAC_WTO_LIMIT_13K))
+
+#define XGMAC_Q0_TX_FLOW_CTRL					0x70
+#define XGMAC_PT_MASK						GENMASK(31, 16)
+#define XGMAC_PAUSE_TIME					FIELD_PREP(XGMAC_PT_MASK, 0xffff)
+#define XGMAC_TFE						BIT(1)
+
+#define XGMAC_RX_FLOW_CTRL					0x90
+#define XGMAC_RFE						BIT(0)
+
+#define XGMAC_MMC_CONTROL					0x800
+#define XGMAC_MCF						BIT(3)
+#define XGMAC_CNTRST						BIT(0)
+
 #endif
diff --git a/include/linux/soc/qcom/ppe.h b/include/linux/soc/qcom/ppe.h
index d3cb18df33fa..40e69a262650 100644
--- a/include/linux/soc/qcom/ppe.h
+++ b/include/linux/soc/qcom/ppe.h
@@ -9,6 +9,7 @@ 
 #define __QCOM_PPE_H__
 
 #include <linux/platform_device.h>
+#include <linux/phylink.h>
 
 /* PPE platform private data, which is used by external driver like
  * Ethernet DMA driver.
@@ -20,6 +21,8 @@  struct ppe_device {
 	struct dentry *debugfs_root;
 	bool is_ppe_probed;
 	void *ppe_priv;
+	struct mutex reg_mutex; /* Protects ppe reg operation */
+	void *ports;
 	void *uniphy;
 };
 
@@ -27,6 +30,36 @@  struct ppe_device {
  * DMA driver to configure PPE.
  */
 struct ppe_device_ops {
+	/*
+	 * PHYLINK integration
+	 */
+	struct phylink *(*phylink_setup)(struct ppe_device *ppe_dev,
+					 struct net_device *netdev, int port);
+	void	(*phylink_destroy)(struct ppe_device *ppe_dev,
+				   int port);
+	void	(*phylink_mac_config)(struct ppe_device *ppe_dev,
+				      int port,
+				      unsigned int mode,
+				      const struct phylink_link_state *state);
+	void	(*phylink_mac_link_up)(struct ppe_device *ppe_dev,
+				       int port,
+				       struct phy_device *phy,
+				       unsigned int mode,
+				       phy_interface_t interface,
+				       int speed,
+				       int duplex,
+				       bool tx_pause,
+				       bool rx_pause);
+	void	(*phylink_mac_link_down)(struct ppe_device *ppe_dev,
+					 int port,
+					 unsigned int mode,
+					 phy_interface_t interface);
+	struct phylink_pcs *(*phylink_mac_select_pcs)(struct ppe_device *ppe_dev,
+						      int port,
+						      phy_interface_t interface);
+	/*
+	 * Port maximum frame size setting
+	 */
 	int	(*set_maxframe)(struct ppe_device *ppe_dev, int port,
 				int maxframe_size);
 };