[][kernel][common][eth][Add phylink rate_matching support for the internal 2.5G PHY]

[Description]
Add phylink rate_matching support for the internal 2.5G PHY.

If without this patch, the phylink may not always enable MAC RX FC
when connected with an internal 2.5G PHY.

[Release-log]
N/A


Change-Id: I326fe52b57cb35d93cbd464970e7cec7b785769c
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7595077
diff --git a/target/linux/mediatek/patches-5.4/999-1712-net-phy-add-phylink-rate-matching-support.patch b/target/linux/mediatek/patches-5.4/999-1712-net-phy-add-phylink-rate-matching-support.patch
new file mode 100644
index 0000000..3b9d2a6
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/999-1712-net-phy-add-phylink-rate-matching-support.patch
@@ -0,0 +1,413 @@
+diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
+index 8406412..96d9e1f 100644
+--- a/drivers/net/phy/phy-core.c
++++ b/drivers/net/phy/phy-core.c
+@@ -62,6 +62,27 @@ const char *phy_duplex_to_str(unsigned int duplex)
+ }
+ EXPORT_SYMBOL_GPL(phy_duplex_to_str);
+ 
++/**
++ * phy_rate_matching_to_str - Return a string describing the rate matching
++ *
++ * @rate_matching: Type of rate matching to describe
++ */
++const char *phy_rate_matching_to_str(int rate_matching)
++{
++	switch (rate_matching) {
++	case RATE_MATCH_NONE:
++		return "none";
++	case RATE_MATCH_PAUSE:
++		return "pause";
++	case RATE_MATCH_CRS:
++		return "crs";
++	case RATE_MATCH_OPEN_LOOP:
++		return "open-loop";
++	}
++	return "Unsupported (update phy-core.c)";
++}
++EXPORT_SYMBOL_GPL(phy_rate_matching_to_str);
++
+ /* A mapping of all SUPPORTED settings to speed/duplex.  This table
+  * must be grouped by speed and sorted in descending match priority
+  * - iow, descending speed. */
+diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
+index e223f0d..6f2e4c7 100644
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -106,6 +106,33 @@ void phy_print_status(struct phy_device *phydev)
+ }
+ EXPORT_SYMBOL(phy_print_status);
+ 
++/**
++ * phy_get_rate_matching - determine if rate matching is supported
++ * @phydev: The phy device to return rate matching for
++ * @iface: The interface mode to use
++ *
++ * This determines the type of rate matching (if any) that @phy supports
++ * using @iface. @iface may be %PHY_INTERFACE_MODE_NA to determine if any
++ * interface supports rate matching.
++ *
++ * Return: The type of rate matching @phy supports for @iface, or
++ *         %RATE_MATCH_NONE.
++ */
++int phy_get_rate_matching(struct phy_device *phydev,
++			  phy_interface_t iface)
++{
++	int ret = RATE_MATCH_NONE;
++
++	if (phydev->drv->get_rate_matching) {
++		mutex_lock(&phydev->lock);
++		ret = phydev->drv->get_rate_matching(phydev, iface);
++		mutex_unlock(&phydev->lock);
++	}
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phy_get_rate_matching);
++
+ /**
+  * phy_clear_interrupt - Ack the phy device's interrupt
+  * @phydev: the phy_device struct
+@@ -380,6 +407,7 @@ void phy_ethtool_ksettings_get(struct phy_device *phydev,
+ 
+ 	cmd->base.speed = phydev->speed;
+ 	cmd->base.duplex = phydev->duplex;
++	cmd->base.rate_matching = phydev->rate_matching;
+ 	if (phydev->interface == PHY_INTERFACE_MODE_MOCA)
+ 		cmd->base.port = PORT_BNC;
+ 	else
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 6b63b98..949e3b8 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -155,6 +155,64 @@ static const char *phylink_an_mode_str(unsigned int mode)
+ 	return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
+ }
+ 
++/**
++ * phylink_interface_max_speed() - get the maximum speed of a phy interface
++ * @interface: phy interface mode defined by &typedef phy_interface_t
++ *
++ * Determine the maximum speed of a phy interface. This is intended to help
++ * determine the correct speed to pass to the MAC when the phy is performing
++ * rate matching.
++ *
++ * Return: The maximum speed of @interface
++ */
++static int phylink_interface_max_speed(phy_interface_t interface)
++{
++	switch (interface) {
++	case PHY_INTERFACE_MODE_RMII:
++	case PHY_INTERFACE_MODE_SMII:
++	case PHY_INTERFACE_MODE_REVMII:
++	case PHY_INTERFACE_MODE_MII:
++		return SPEED_100;
++
++	case PHY_INTERFACE_MODE_TBI:
++	case PHY_INTERFACE_MODE_MOCA:
++	case PHY_INTERFACE_MODE_RTBI:
++	case PHY_INTERFACE_MODE_1000BASEX:
++	case PHY_INTERFACE_MODE_TRGMII:
++	case PHY_INTERFACE_MODE_RGMII_TXID:
++	case PHY_INTERFACE_MODE_RGMII_RXID:
++	case PHY_INTERFACE_MODE_RGMII_ID:
++	case PHY_INTERFACE_MODE_RGMII:
++	case PHY_INTERFACE_MODE_QSGMII:
++	case PHY_INTERFACE_MODE_SGMII:
++	case PHY_INTERFACE_MODE_GMII:
++		return SPEED_1000;
++
++	case PHY_INTERFACE_MODE_2500BASEX:
++		return SPEED_2500;
++
++	case PHY_INTERFACE_MODE_5GBASER:
++		return SPEED_5000;
++
++	case PHY_INTERFACE_MODE_XGMII:
++	case PHY_INTERFACE_MODE_RXAUI:
++	case PHY_INTERFACE_MODE_XAUI:
++	case PHY_INTERFACE_MODE_10GKR:
++	case PHY_INTERFACE_MODE_USXGMII:
++		return SPEED_10000;
++
++	case PHY_INTERFACE_MODE_INTERNAL:
++	case PHY_INTERFACE_MODE_NA:
++	case PHY_INTERFACE_MODE_MAX:
++		/* No idea! Garbage in, unknown out */
++		return SPEED_UNKNOWN;
++	}
++
++	/* If we get here, someone forgot to add an interface mode above */
++	WARN_ON_ONCE(1);
++	return SPEED_UNKNOWN;
++}
++
+ static int phylink_validate_mac_and_pcs(struct phylink *pl,
+ 					unsigned long *supported,
+ 					struct phylink_link_state *state)
+@@ -402,11 +460,12 @@ static void phylink_mac_config(struct phylink *pl,
+ 			       const struct phylink_link_state *state)
+ {
+ 	phylink_dbg(pl,
+-		    "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
++		    "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
+ 		    __func__, phylink_an_mode_str(pl->cur_link_an_mode),
+ 		    phy_modes(state->interface),
+ 		    phy_speed_to_str(state->speed),
+ 		    phy_duplex_to_str(state->duplex),
++		    phy_rate_matching_to_str(state->rate_matching),
+ 		    __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
+ 		    state->pause, state->link, state->an_enabled);
+ 
+@@ -499,6 +558,7 @@ static void phylink_mac_pcs_get_state(struct phylink *pl,
+ 	linkmode_zero(state->lp_advertising);
+ 	state->interface = pl->link_config.interface;
+ 	state->an_enabled = pl->link_config.an_enabled;
++	state->rate_matching = pl->link_config.rate_matching;
+ 	if (state->an_enabled) {
+ 		state->speed = SPEED_UNKNOWN;
+ 		state->duplex = DUPLEX_UNKNOWN;
+@@ -614,9 +674,32 @@ static void phylink_link_up(struct phylink *pl,
+ {
+ 	struct net_device *ndev = pl->netdev;
+ 	int speed, duplex;
++	bool rx_pause;
+ 
+ 	speed = link_state.speed;
+ 	duplex = link_state.duplex;
++	rx_pause = !!(link_state.pause & MLO_PAUSE_RX);
++
++	switch (link_state.rate_matching) {
++	case RATE_MATCH_PAUSE:
++		/* The PHY is doing rate matchion from the media rate (in
++		 * the link_state) to the interface speed, and will send
++		 * pause frames to the MAC to limit its transmission speed.
++		 */
++		speed = phylink_interface_max_speed(link_state.interface);
++		duplex = DUPLEX_FULL;
++		rx_pause = true;
++		break;
++
++	case RATE_MATCH_CRS:
++		/* The PHY is doing rate matchion from the media rate (in
++		 * the link_state) to the interface speed, and will cause
++		 * collisions to the MAC to limit its transmission speed.
++		 */
++		speed = phylink_interface_max_speed(link_state.interface);
++		duplex = DUPLEX_HALF;
++		break;
++	}
+ 
+ 	pl->cur_interface = link_state.interface;
+ 
+@@ -626,8 +709,7 @@ static void phylink_link_up(struct phylink *pl,
+ 
+ 	pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
+ 				 pl->cur_interface, speed, duplex,
+-				 !!(link_state.pause & MLO_PAUSE_TX),
+-				 !!(link_state.pause & MLO_PAUSE_RX));
++				 !!(link_state.pause & MLO_PAUSE_TX), rx_pause);
+ 
+ 	if (ndev)
+ 		netif_carrier_on(ndev);
+@@ -719,6 +801,17 @@ static void phylink_resolve(struct work_struct *w)
+ 				}
+ 				link_state.interface = pl->phy_state.interface;
+ 
++				/* If we are doing rate matching, then the
++				 * link speed/duplex comes from the PHY
++				 */
++				if (pl->phy_state.rate_matching) {
++					link_state.rate_matching =
++						pl->phy_state.rate_matching;
++					link_state.speed = pl->phy_state.speed;
++					link_state.duplex =
++						pl->phy_state.duplex;
++				}
++
+ 				/* If we have a PHY, we need to update with
+ 				 * the PHY flow control bits.
+ 				 */
+@@ -935,6 +1028,7 @@ static void phylink_phy_change(struct phy_device *phydev, bool up,
+ 	mutex_lock(&pl->state_mutex);
+ 	pl->phy_state.speed = phydev->speed;
+ 	pl->phy_state.duplex = phydev->duplex;
++	pl->phy_state.rate_matching = phydev->rate_matching;
+ 	pl->phy_state.pause = MLO_PAUSE_NONE;
+ 	if (phydev->pause)
+ 		pl->phy_state.pause |= MLO_PAUSE_SYM;
+@@ -946,10 +1040,11 @@ static void phylink_phy_change(struct phy_device *phydev, bool up,
+ 
+ 	phylink_run_resolve(pl);
+ 
+-	phylink_dbg(pl, "phy link %s %s/%s/%s\n", up ? "up" : "down",
++	phylink_dbg(pl, "phy link %s %s/%s/%s/%s\n", up ? "up" : "down",
+ 		    phy_modes(phydev->interface),
+ 		    phy_speed_to_str(phydev->speed),
+-		    phy_duplex_to_str(phydev->duplex));
++		    phy_duplex_to_str(phydev->duplex),
++		    phy_rate_matching_to_str(phydev->rate_matching));
+ }
+ 
+ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
+@@ -971,6 +1066,12 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
+ 	memset(&config, 0, sizeof(config));
+ 	linkmode_copy(supported, phy->supported);
+ 	linkmode_copy(config.advertising, phy->advertising);
++
++	/* Check whether we would use rate matching for the proposed interface
++	 * mode.
++	 */
++	config.rate_matching = phy_get_rate_matching(phy, interface);
++
+ 	config.interface = interface;
+ 
+ 	ret = phylink_validate(pl, supported, &config);
+@@ -988,6 +1089,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
+ 	mutex_lock(&pl->state_mutex);
+ 	pl->phydev = phy;
+ 	pl->phy_state.interface = interface;
++	pl->phy_state.rate_matching = RATE_MATCH_NONE;
+ 	linkmode_copy(pl->supported, supported);
+ 	linkmode_copy(pl->link_config.advertising, config.advertising);
+ 
+@@ -1348,8 +1450,10 @@ static void phylink_get_ksettings(const struct phylink_link_state *state,
+ {
+ 	phylink_merge_link_mode(kset->link_modes.advertising, state->advertising);
+ 	linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising);
+-	kset->base.speed = state->speed;
+-	kset->base.duplex = state->duplex;
++	if (kset->base.rate_matching == RATE_MATCH_NONE) {
++		kset->base.speed = state->speed;
++		kset->base.duplex = state->duplex;
++	}
+ 	kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
+ 				AUTONEG_DISABLE;
+ }
+diff --git a/include/linux/phy.h b/include/linux/phy.h
+index 4f2c105..df9fb90 100644
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -423,6 +423,8 @@ struct phy_device {
+ 	/* Interrupts are enabled */
+ 	unsigned interrupts:1;
+ 
++	int rate_matching;
++
+ 	enum phy_state state;
+ 
+ 	u32 dev_flags;
+@@ -544,6 +546,18 @@ struct phy_driver {
+ 	 */
+ 	int (*get_features)(struct phy_device *phydev);
+ 
++	/**
++	 * @get_rate_matching: Get the supported type of rate matching for a
++	 * particular phy interface. This is used by phy consumers to determine
++	 * whether to advertise lower-speed modes for that interface. It is
++	 * assumed that if a rate matching mode is supported on an interface,
++	 * then that interface's rate can be adapted to all slower link speeds
++	 * supported by the phy. If the interface is not supported, this should
++	 * return %RATE_MATCH_NONE.
++	 */
++	int (*get_rate_matching)(struct phy_device *phydev,
++				   phy_interface_t iface);
++
+ 	/* PHY Power Management */
+ 	int (*suspend)(struct phy_device *phydev);
+ 	int (*resume)(struct phy_device *phydev);
+@@ -704,6 +718,7 @@ struct phy_fixup {
+ 
+ const char *phy_speed_to_str(int speed);
+ const char *phy_duplex_to_str(unsigned int duplex);
++const char *phy_rate_matching_to_str(int rate_matching);
+ 
+ /* A structure for mapping a particular speed and duplex
+  * combination to a particular SUPPORTED and ADVERTISED value
+@@ -1242,6 +1257,8 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd);
+ void phy_request_interrupt(struct phy_device *phydev);
+ void phy_free_interrupt(struct phy_device *phydev);
+ void phy_print_status(struct phy_device *phydev);
++int phy_get_rate_matching(struct phy_device *phydev,
++			    phy_interface_t iface);
+ int phy_set_max_speed(struct phy_device *phydev, u32 max_speed);
+ void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode);
+ void phy_advertise_supported(struct phy_device *phydev);
+diff --git a/include/linux/phylink.h b/include/linux/phylink.h
+index 48ff9fe..e1c022f 100644
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -38,6 +38,10 @@ static inline bool phylink_autoneg_inband(unsigned int mode)
+  * @speed: link speed, one of the SPEED_* constants.
+  * @duplex: link duplex mode, one of DUPLEX_* constants.
+  * @pause: link pause state, described by MLO_PAUSE_* constants.
++ * @rate_matching: rate matching being performed, one of the RATE_MATCH_*
++ *   constants. If rate matching is taking place, then the speed/duplex of
++ *   the medium link mode (@speed and @duplex) and the speed/duplex of the phy
++ *   interface mode (@interface) are different.
+  * @link: true if the link is up.
+  * @an_enabled: true if autonegotiation is enabled/desired.
+  * @an_complete: true if autonegotiation has completed.
+@@ -49,6 +53,7 @@ struct phylink_link_state {
+ 	int speed;
+ 	int duplex;
+ 	int pause;
++	int rate_matching;
+ 	unsigned int link:1;
+ 	unsigned int an_enabled:1;
+ 	unsigned int an_complete:1;
+diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
+index 8d465e5..ce41d2f 100644
+--- a/include/uapi/linux/ethtool.h
++++ b/include/uapi/linux/ethtool.h
+@@ -1643,6 +1643,20 @@ static inline int ethtool_validate_duplex(__u8 duplex)
+ 	return 0;
+ }
+ 
++/* These are used to throttle the rate of data on the phy interface when the
++ * native speed of the interface is higher than the link speed. These should
++ * not be used for phy interfaces which natively support multiple speeds (e.g.
++ * MII or SGMII).
++ */
++/* No rate matching performed. */
++#define RATE_MATCH_NONE		0
++/* The phy sends pause frames to throttle the MAC. */
++#define RATE_MATCH_PAUSE	1
++/* The phy asserts CRS to prevent the MAC from transmitting. */
++#define RATE_MATCH_CRS		2
++/* The MAC is programmed with a sufficiently-large IPG. */
++#define RATE_MATCH_OPEN_LOOP	3
++
+ /* Which connector port. */
+ #define PORT_TP			0x00
+ #define PORT_AUI		0x01
+@@ -1832,6 +1846,7 @@ enum ethtool_reset_flags {
+  *	autonegotiation; 0 if unknown or not applicable.  Read-only.
+  * @transceiver: Used to distinguish different possible PHY types,
+  *	reported consistently by PHYLIB.  Read-only.
++ * @rate_matching: Rate adaptation performed by the PHY
+  *
+  * If autonegotiation is disabled, the speed and @duplex represent the
+  * fixed link mode and are writable if the driver supports multiple
+@@ -1879,7 +1894,8 @@ struct ethtool_link_settings {
+ 	__u8	eth_tp_mdix_ctrl;
+ 	__s8	link_mode_masks_nwords;
+ 	__u8	transceiver;
+-	__u8	reserved1[3];
++	__u8	reserved1[2];
++	__u8	rate_matching;
+ 	__u32	reserved[7];
+ 	__u32	link_mode_masks[0];
+ 	/* layout of link_mode_masks fields:
+diff --git a/net/core/ethtool.c b/net/core/ethtool.c
+index 9ae38c3..41a8b1d 100644
+--- a/net/core/ethtool.c
++++ b/net/core/ethtool.c
+@@ -656,6 +656,7 @@ static int ethtool_get_link_ksettings(struct net_device *dev,
+ 	link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
+ 	link_ksettings.base.link_mode_masks_nwords
+ 		= __ETHTOOL_LINK_MODE_MASK_NU32;
++	link_ksettings.base.rate_matching = RATE_MATCH_NONE;
+ 
+ 	return store_link_ksettings_for_user(useraddr, &link_ksettings);
+ }