Paste #381333

From cba245cdf4696ef2ce13dca7af27385a3bcec640 Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Wed, 9 Feb 2022 09:05:15 +0200
Subject: [PATCH 01/11] mvebu: add support for Marvell 88E6393X - Amethyst

These patches add support for Marvell 88E6393X - Amethyst. Its code is backported from kernel 5.15.
Also, these patches contain support for the strict-cpu-mode for the switch. In this mode, all
traffic is passed through CPU(switch ports mac learning is disabled). This is necessary to plug
two different DSA ports of the device in the same switch.

Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 .../706-net-phy-marvell-88e6393.patch         |  225 ++
 ...ernet-marvell-mvpp2-get-mac-from-dtb.patch |   20 +
 .../708-net-dsa-strict-cpu-mode.patch         |   27 +
 ...net-bridge-allow-br-fdb-local-notify.patch |   13 +
 ...t-dsa-mv88e6xxx-support-for-88e6393x.patch | 2164 +++++++++++++++++
 5 files changed, 2449 insertions(+)
 create mode 100644 target/linux/mvebu/patches-5.10/706-net-phy-marvell-88e6393.patch
 create mode 100644 target/linux/mvebu/patches-5.10/707-net-ethernet-marvell-mvpp2-get-mac-from-dtb.patch
 create mode 100644 target/linux/mvebu/patches-5.10/708-net-dsa-strict-cpu-mode.patch
 create mode 100644 target/linux/mvebu/patches-5.10/709-net-bridge-allow-br-fdb-local-notify.patch
 create mode 100644 target/linux/mvebu/patches-5.10/710-net-dsa-mv88e6xxx-support-for-88e6393x.patch

diff --git a/target/linux/mvebu/patches-5.10/706-net-phy-marvell-88e6393.patch b/target/linux/mvebu/patches-5.10/706-net-phy-marvell-88e6393.patch
new file mode 100644
index 0000000000..9a6637abbf
--- /dev/null
+++ b/target/linux/mvebu/patches-5.10/706-net-phy-marvell-88e6393.patch
@@ -0,0 +1,225 @@
+diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
+index bc7645e..a3d4d51 100644
+--- a/drivers/net/phy/marvell.c
++++ b/drivers/net/phy/marvell.c
+@@ -117,8 +117,21 @@
+ #define MII_88E6390_MISC_TEST_SAMPLE_DISABLE	BIT(15)
+ #define MII_88E6390_MISC_TEST_SAMPLE_ENABLE	0
+ #define MII_88E6390_MISC_TEST_SAMPLE_MASK	(0x3 << 14)
++#define MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE		(0x1 << 14)
++#define MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK MII_88E6390_MISC_TEST_SAMPLE_MASK
++#define MII_88E6393_MISC_TEST_SAMPLES_2048	(0x0 << 11)
++#define MII_88E6393_MISC_TEST_SAMPLES_4096	(0x1 << 11)
++#define MII_88E6393_MISC_TEST_SAMPLES_8192	(0x2 << 11)
++#define MII_88E6393_MISC_TEST_SAMPLES_16384	(0x3 << 11)
++#define MII_88E6393_MISC_TEST_SAMPLES_MASK	(0x3 << 11)
++#define MII_88E6393_MISC_TEST_RATE_2_3MS	(0x5 << 8)
++#define MII_88E6393_MISC_TEST_RATE_6_4MS	(0x6 << 8)
++#define MII_88E6393_MISC_TEST_RATE_11_9MS	(0x7 << 8)
++#define MII_88E6393_MISC_TEST_RATE_MASK		(0x7 << 8)
+ 
+ #define MII_88E6390_TEMP_SENSOR		0x1c
++#define MII_88E6393_TEMP_SENSOR_THRESHOLD_MASK	0xff00
++#define MII_88E6393_TEMP_SENSOR_THRESHOLD_SHIFT	8
+ #define MII_88E6390_TEMP_SENSOR_MASK	0xff
+ #define MII_88E6390_TEMP_SENSOR_SAMPLES 10
+ 
+@@ -263,6 +276,8 @@
+ 
+ #define NB_FIBER_STATS	1
+ 
++#define MARVELL_PHY_ID_88E6393_FAMILY	0x002b0b9b
++
+ MODULE_DESCRIPTION("Marvell PHY driver");
+ MODULE_AUTHOR("Andy Fleming");
+ MODULE_LICENSE("GPL");
+@@ -333,6 +348,22 @@ static int marvell_config_intr(struct phy_device *phydev)
+ 	return err;
+ }
+ 
++static void phy_trigger_machine(struct phy_device *phydev)
++{
++	phy_queue_state_machine(phydev, 0);
++}
++
++static void phy_error(struct phy_device *phydev)
++{
++	WARN_ON(1);
++
++	mutex_lock(&phydev->lock);
++	phydev->state = PHY_HALTED;
++	mutex_unlock(&phydev->lock);
++
++	phy_trigger_machine(phydev);
++}
++
+ static int marvell_set_polarity(struct phy_device *phydev, int polarity)
+ {
+ 	int reg;
+@@ -958,6 +989,17 @@ static int m88e1011_set_downshift(struct phy_device *phydev, u8 cnt)
+ 	return genphy_soft_reset(phydev);
+ }
+ 
++static int marvell_1011gbe_config_init(struct phy_device *phydev)
++{
++	int err;
++
++	err = m88e1011_set_downshift(phydev, 3);
++	if (err < 0)
++		return err;
++
++	return marvell_config_init(phydev);
++}
++
+ static int m88e1011_get_tunable(struct phy_device *phydev,
+ 				struct ethtool_tunable *tuna, void *data)
+ {
+@@ -2144,6 +2186,7 @@ static int marvell_vct7_cable_test_get_status(struct phy_device *phydev,
+ 
+ #ifdef CONFIG_HWMON
+ struct marvell_hwmon_ops {
++	int (*config)(struct phy_device *phydev);
+ 	int (*get_temp)(struct phy_device *phydev, long *temp);
+ 	int (*get_temp_critical)(struct phy_device *phydev, long *temp);
+ 	int (*set_temp_critical)(struct phy_device *phydev, long temp);
+@@ -2319,6 +2362,65 @@ error:
+ 	return ret;
+ }
+ 
++static int m88e6393_get_temp(struct phy_device *phydev, long *temp)
++{
++	int err;
++
++	err = m88e1510_get_temp(phydev, temp);
++
++	/* 88E1510 measures T + 25, while the PHY on 88E6393X switch
++	 * T + 75, so we have to subtract another 50
++	 */
++	*temp -= 50000;
++
++	return err;
++}
++
++static int m88e6393_get_temp_critical(struct phy_device *phydev, long *temp)
++{
++	int ret;
++
++	*temp = 0;
++
++	ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
++			     MII_88E6390_TEMP_SENSOR);
++	if (ret < 0)
++		return ret;
++
++	*temp = (((ret & MII_88E6393_TEMP_SENSOR_THRESHOLD_MASK) >>
++		  MII_88E6393_TEMP_SENSOR_THRESHOLD_SHIFT) - 75) * 1000;
++
++	return 0;
++}
++
++static int m88e6393_set_temp_critical(struct phy_device *phydev, long temp)
++{
++	temp = (temp / 1000) + 75;
++
++	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
++				MII_88E6390_TEMP_SENSOR,
++				MII_88E6393_TEMP_SENSOR_THRESHOLD_MASK,
++				temp << MII_88E6393_TEMP_SENSOR_THRESHOLD_SHIFT);
++}
++
++static int m88e6393_hwmon_config(struct phy_device *phydev)
++{
++	int err;
++
++	err = m88e6393_set_temp_critical(phydev, 100000);
++	if (err)
++		return err;
++
++	return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
++				MII_88E6390_MISC_TEST,
++				MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK |
++				MII_88E6393_MISC_TEST_SAMPLES_MASK |
++				MII_88E6393_MISC_TEST_RATE_MASK,
++				MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE |
++				MII_88E6393_MISC_TEST_SAMPLES_2048 |
++				MII_88E6393_MISC_TEST_RATE_2_3MS);
++}
++
+ static int marvell_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ 			      u32 attr, int channel, long *temp)
+ {
+@@ -2463,10 +2565,16 @@ static int marvell_hwmon_probe(struct phy_device *phydev)
+ 
+ 	priv->hwmon_dev = devm_hwmon_device_register_with_info(
+ 		dev, priv->hwmon_name, phydev, &marvell_hwmon_chip_info, NULL);
++	if (IS_ERR(priv->hwmon_dev))
++		return PTR_ERR(priv->hwmon_dev);
++
++	if (ops->config)
++		err = ops->config(phydev);
+ 
+-	return PTR_ERR_OR_ZERO(priv->hwmon_dev);
++	return err;
+ }
+ 
++
+ static const struct marvell_hwmon_ops m88e1121_hwmon_ops = {
+ 	.get_temp = m88e1121_get_temp,
+ };
+@@ -2482,6 +2590,14 @@ static const struct marvell_hwmon_ops m88e6390_hwmon_ops = {
+ 	.get_temp = m88e6390_get_temp,
+ };
+ 
++static const struct marvell_hwmon_ops m88e6393_hwmon_ops = {
++	.config = m88e6393_hwmon_config,
++	.get_temp = m88e6393_get_temp,
++	.get_temp_critical = m88e6393_get_temp_critical,
++	.set_temp_critical = m88e6393_set_temp_critical,
++	.get_temp_alarm = m88e1510_get_temp_alarm,
++};
++
+ #define DEF_MARVELL_HWMON_OPS(s) (&(s))
+ 
+ #else
+@@ -2958,6 +3074,33 @@ static struct phy_driver marvell_drivers[] = {
+ 		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
+ 		.cable_test_get_status = marvell_vct7_cable_test_get_status,
+ 	},
++	{
++		.phy_id = MARVELL_PHY_ID_88E6393_FAMILY,
++		.phy_id_mask = MARVELL_PHY_ID_MASK,
++		.name = "Marvell 88E6393 Family",
++		.driver_data = DEF_MARVELL_HWMON_OPS(m88e6393_hwmon_ops),
++		/* PHY_GBIT_FEATURES */
++		.flags = PHY_POLL_CABLE_TEST,
++		.probe = marvell_probe,
++		.config_init = marvell_1011gbe_config_init,
++		.config_aneg = m88e1510_config_aneg,
++		.read_status = marvell_read_status,
++		.ack_interrupt = marvell_ack_interrupt,
++		.config_intr = marvell_config_intr,
++		.did_interrupt = m88e1121_did_interrupt,
++		.resume = genphy_resume,
++		.suspend = genphy_suspend,
++		.read_page = marvell_read_page,
++		.write_page = marvell_write_page,
++		.get_sset_count = marvell_get_sset_count,
++		.get_strings = marvell_get_strings,
++		.get_stats = marvell_get_stats,
++		.get_tunable = m88e1540_get_tunable,
++		.set_tunable = m88e1540_set_tunable,
++		.cable_test_start = marvell_vct7_cable_test_start,
++		.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
++		.cable_test_get_status = marvell_vct7_cable_test_get_status,
++	},
+ 	{
+ 		.phy_id = MARVELL_PHY_ID_88E1340S,
+ 		.phy_id_mask = MARVELL_PHY_ID_MASK,
+@@ -3025,6 +3168,7 @@ static struct mdio_device_id __maybe_unused marvell_tbl[] = {
+ 	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
+ 	{ MARVELL_PHY_ID_88E6341_FAMILY, MARVELL_PHY_ID_MASK },
+ 	{ MARVELL_PHY_ID_88E6390_FAMILY, MARVELL_PHY_ID_MASK },
++	{ MARVELL_PHY_ID_88E6393_FAMILY, MARVELL_PHY_ID_MASK },
+ 	{ MARVELL_PHY_ID_88E1340S, MARVELL_PHY_ID_MASK },
+ 	{ MARVELL_PHY_ID_88E1548P, MARVELL_PHY_ID_MASK },
+ 	{ }
diff --git a/target/linux/mvebu/patches-5.10/707-net-ethernet-marvell-mvpp2-get-mac-from-dtb.patch b/target/linux/mvebu/patches-5.10/707-net-ethernet-marvell-mvpp2-get-mac-from-dtb.patch
new file mode 100644
index 0000000000..86ad7cc0b3
--- /dev/null
+++ b/target/linux/mvebu/patches-5.10/707-net-ethernet-marvell-mvpp2-get-mac-from-dtb.patch
@@ -0,0 +1,20 @@
+diff -rNu a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c	2022-01-18 08:46:10.299944874 +0200
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c	2022-01-18 09:15:41.993041235 +0200
+@@ -5727,6 +5727,16 @@
+ 		}
+ 	}
+ 
++	{
++		struct device_node *np = to_of_node(fwnode);
++		of_get_mac_address(np, hw_mac_addr);
++		if (is_valid_ether_addr(hw_mac_addr)) {
++			*mac_from = "dtb";
++			ether_addr_copy(dev->dev_addr, hw_mac_addr);
++			return;
++		}
++	}
++
+ 	*mac_from = "random";
+ 	eth_hw_addr_random(dev);
+ }
diff --git a/target/linux/mvebu/patches-5.10/708-net-dsa-strict-cpu-mode.patch b/target/linux/mvebu/patches-5.10/708-net-dsa-strict-cpu-mode.patch
new file mode 100644
index 0000000000..d638770cd3
--- /dev/null
+++ b/target/linux/mvebu/patches-5.10/708-net-dsa-strict-cpu-mode.patch
@@ -0,0 +1,27 @@
+diff -rNu a/include/net/dsa.h b/include/net/dsa.h
+--- a/include/net/dsa.h	2022-01-19 11:06:49.163794543 +0200
++++ b/include/net/dsa.h	2022-01-19 11:05:42.891832804 +0200
+@@ -214,6 +214,7 @@
+ 	struct device_node	*dn;
+ 	unsigned int		ageing_time;
+ 	bool			vlan_filtering;
++	bool			strict_cpu_mode;
+ 	u8			stp_state;
+ 	struct net_device	*bridge_dev;
+ 	struct devlink_port	devlink_port;
+diff -rNu a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c
+--- a/net/dsa/tag_dsa.c	2022-01-16 10:14:24.000000000 +0200
++++ b/net/dsa/tag_dsa.c	2022-01-19 11:58:49.877936826 +0200
+@@ -131,8 +131,10 @@
+ 			skb->data - ETH_HLEN - DSA_HLEN,
+ 			2 * ETH_ALEN);
+ 	}
+-
+-	skb->offload_fwd_mark = 1;
++	if (likely(dev->dsa_ptr->strict_cpu_mode))
++		skb->offload_fwd_mark = 0;
++	else
++		skb->offload_fwd_mark = 1;
+ 
+ 	return skb;
+ }
diff --git a/target/linux/mvebu/patches-5.10/709-net-bridge-allow-br-fdb-local-notify.patch b/target/linux/mvebu/patches-5.10/709-net-bridge-allow-br-fdb-local-notify.patch
new file mode 100644
index 0000000000..070f1ab818
--- /dev/null
+++ b/target/linux/mvebu/patches-5.10/709-net-bridge-allow-br-fdb-local-notify.patch
@@ -0,0 +1,13 @@
+diff -rNu a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
+--- a/net/bridge/br_switchdev.c	2022-01-19 14:53:07.059595555 +0200
++++ b/net/bridge/br_switchdev.c	2022-01-19 14:54:39.034486230 +0200
+@@ -115,9 +115,6 @@
+ 	};
+ 	struct net_device *dev = fdb->dst ? fdb->dst->dev : br->dev;
+ 
+-	if (test_bit(BR_FDB_LOCAL, &fdb->flags))
+-		return;
+-
+ 	switch (type) {
+ 	case RTM_DELNEIGH:
+ 		call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
diff --git a/target/linux/mvebu/patches-5.10/710-net-dsa-mv88e6xxx-support-for-88e6393x.patch b/target/linux/mvebu/patches-5.10/710-net-dsa-mv88e6xxx-support-for-88e6393x.patch
new file mode 100644
index 0000000000..22da3048c5
--- /dev/null
+++ b/target/linux/mvebu/patches-5.10/710-net-dsa-mv88e6xxx-support-for-88e6393x.patch
@@ -0,0 +1,2164 @@
+diff -rNu a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
+--- a/drivers/net/dsa/mv88e6xxx/chip.c	2022-01-20 08:19:34.897902121 +0200
++++ b/drivers/net/dsa/mv88e6xxx/chip.c	2022-01-20 08:11:18.032762795 +0200
+@@ -485,12 +485,12 @@
+ 					  struct phylink_link_state *state)
+ {
+ 	struct mv88e6xxx_chip *chip = ds->priv;
+-	u8 lane;
++	int lane;
+ 	int err;
+ 
+ 	mv88e6xxx_reg_lock(chip);
+ 	lane = mv88e6xxx_serdes_get_lane(chip, port);
+-	if (lane && chip->info->ops->serdes_pcs_get_state)
++	if (lane >= 0 && chip->info->ops->serdes_pcs_get_state)
+ 		err = chip->info->ops->serdes_pcs_get_state(chip, port, lane,
+ 							    state);
+ 	else
+@@ -506,11 +506,11 @@
+ 				       const unsigned long *advertise)
+ {
+ 	const struct mv88e6xxx_ops *ops = chip->info->ops;
+-	u8 lane;
++	int lane;
+ 
+ 	if (ops->serdes_pcs_config) {
+ 		lane = mv88e6xxx_serdes_get_lane(chip, port);
+-		if (lane)
++		if (lane >= 0)
+ 			return ops->serdes_pcs_config(chip, port, lane, mode,
+ 						      interface, advertise);
+ 	}
+@@ -523,14 +523,14 @@
+ 	struct mv88e6xxx_chip *chip = ds->priv;
+ 	const struct mv88e6xxx_ops *ops;
+ 	int err = 0;
+-	u8 lane;
++	int lane;
+ 
+ 	ops = chip->info->ops;
+ 
+ 	if (ops->serdes_pcs_an_restart) {
+ 		mv88e6xxx_reg_lock(chip);
+ 		lane = mv88e6xxx_serdes_get_lane(chip, port);
+-		if (lane)
++		if (lane >= 0)
+ 			err = ops->serdes_pcs_an_restart(chip, port, lane);
+ 		mv88e6xxx_reg_unlock(chip);
+ 
+@@ -544,11 +544,11 @@
+ 					int speed, int duplex)
+ {
+ 	const struct mv88e6xxx_ops *ops = chip->info->ops;
+-	u8 lane;
++	int lane;
+ 
+ 	if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) {
+ 		lane = mv88e6xxx_serdes_get_lane(chip, port);
+-		if (lane)
++		if (lane >= 0)
+ 			return ops->serdes_pcs_link_up(chip, port, lane,
+ 						       speed, duplex);
+ 	}
+@@ -635,6 +635,32 @@
+ 	mv88e6390_phylink_validate(chip, port, mask, state);
+ }
+ 
++static void mv88e6393x_phylink_validate(struct mv88e6xxx_chip *chip, int port,
++					unsigned long *mask,
++					struct phylink_link_state *state)
++{
++	bool is_6191x =
++		chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X;
++
++	if (((port == 0 || port == 9) && !is_6191x) || port == 10) {
++		phylink_set(mask, 10000baseT_Full);
++		phylink_set(mask, 10000baseKR_Full);
++		phylink_set(mask, 10000baseCR_Full);
++		phylink_set(mask, 10000baseSR_Full);
++		phylink_set(mask, 10000baseLR_Full);
++		phylink_set(mask, 10000baseLRM_Full);
++		phylink_set(mask, 10000baseER_Full);
++		phylink_set(mask, 5000baseT_Full);
++		phylink_set(mask, 2500baseX_Full);
++		phylink_set(mask, 2500baseT_Full);
++	}
++
++	phylink_set(mask, 1000baseT_Full);
++	phylink_set(mask, 1000baseX_Full);
++
++	mv88e6065_phylink_validate(chip, port, mask, state);
++}
++
+ static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
+ 			       unsigned long *supported,
+ 			       struct phylink_link_state *state)
+@@ -2243,6 +2269,9 @@
+ 	struct mv88e6xxx_chip *chip = ds->priv;
+ 	int err;
+ 
++	if (likely(chip->strict_cpu_mode))
++		return 0;
++
+ 	mv88e6xxx_reg_lock(chip);
+ 	err = mv88e6xxx_bridge_map(chip, br);
+ 	mv88e6xxx_reg_unlock(chip);
+@@ -2255,6 +2284,9 @@
+ {
+ 	struct mv88e6xxx_chip *chip = ds->priv;
+ 
++	if (likely(chip->strict_cpu_mode))
++		return;
++
+ 	mv88e6xxx_reg_lock(chip);
+ 	if (mv88e6xxx_bridge_map(chip, br) ||
+ 	    mv88e6xxx_port_vlan_map(chip, port))
+@@ -2425,7 +2457,10 @@
+ 	bool flood;
+ 
+ 	/* Upstream ports flood frames with unknown unicast or multicast DA */
+-	flood = dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port);
++	if (likely(chip->strict_cpu_mode))
++		flood = dsa_is_cpu_port(ds, port);
++	else
++		flood = dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port);
+ 	if (chip->info->ops->port_set_egress_floods)
+ 		return chip->info->ops->port_set_egress_floods(chip, port,
+ 							       flood, flood);
+@@ -2439,11 +2474,11 @@
+ 	struct mv88e6xxx_chip *chip = mvp->chip;
+ 	irqreturn_t ret = IRQ_NONE;
+ 	int port = mvp->port;
+-	u8 lane;
++	int lane;
+ 
+ 	mv88e6xxx_reg_lock(chip);
+ 	lane = mv88e6xxx_serdes_get_lane(chip, port);
+-	if (lane)
++	if (lane >= 0)
+ 		ret = mv88e6xxx_serdes_irq_status(chip, port, lane);
+ 	mv88e6xxx_reg_unlock(chip);
+ 
+@@ -2451,7 +2486,7 @@
+ }
+ 
+ static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port,
+-					u8 lane)
++					int lane)
+ {
+ 	struct mv88e6xxx_port *dev_id = &chip->ports[port];
+ 	unsigned int irq;
+@@ -2480,7 +2515,7 @@
+ }
+ 
+ static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port,
+-				     u8 lane)
++				     int lane)
+ {
+ 	struct mv88e6xxx_port *dev_id = &chip->ports[port];
+ 	unsigned int irq = dev_id->serdes_irq;
+@@ -2502,14 +2537,15 @@
+ 	return err;
+ }
+ 
++
+ static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,
+ 				  bool on)
+ {
+-	u8 lane;
++	int lane;
+ 	int err;
+ 
+ 	lane = mv88e6xxx_serdes_get_lane(chip, port);
+-	if (!lane)
++	if (lane < 0)
+ 		return 0;
+ 
+ 	if (on) {
+@@ -2569,6 +2605,28 @@
+ 	return 0;
+ }
+ 
++static int mv88e6xxx_port_set_learn_disabled(struct mv88e6xxx_chip *chip, int port, int disabled)
++{
++	int err;
++	u16 reg;
++
++	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN,
++					  &reg);
++	if (err)
++		return err;
++
++	if (disabled)
++		reg |= MV88E6XXX_PORT_BASE_VLAN_LEARN_DISABLE;
++	else
++		reg &= ~MV88E6XXX_PORT_BASE_VLAN_LEARN_DISABLE;
++
++	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_BASE_VLAN,
++				   reg);
++
++	return err;
++}
++
++
+ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
+ {
+ 	struct dsa_switch *ds = chip->ds;
+@@ -2649,15 +2707,26 @@
+ 			return err;
+ 	}
+ 
+-	/* Port Association Vector: when learning source addresses
+-	 * of packets, add the address to the address database using
+-	 * a port bitmap that has only the bit for this port set and
+-	 * the other bits clear.
+-	 */
+-	reg = 1 << port;
+-	/* Disable learning for CPU port */
+-	if (dsa_is_cpu_port(ds, port))
++	if (likely(chip->strict_cpu_mode)) {
++		/* set to 0 VLAN_BASE_LEARN_DISABLED bit - this is very important
++			 for disable learning via PORT_ASSOC_VECTOR reg. otherwise, there
++			 will be a flood from the CPU port to all ports !!!  */
++		err = mv88e6xxx_port_set_learn_disabled(chip, port, 0);
++		if (err)
++			return err;
++		/* Disable learning for all ports */
+ 		reg = 0;
++	} else {
++		/* Port Association Vector: when learning source addresses
++		 * of packets, add the address to the address database using
++		 * a port bitmap that has only the bit for this port set and
++		 * the other bits clear.
++		 */
++		reg = 1 << port;
++		/* Disable learning for CPU port */
++		if (dsa_is_cpu_port(ds, port))
++			reg = 0;
++	}
+ 
+ 	/* Disable ATU member violation interrupt */
+ 	reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG;
+@@ -3016,6 +3085,7 @@
+ static const u16 family_prod_id_table[] = {
+ 	[MV88E6XXX_FAMILY_6341] = MV88E6XXX_PORT_SWITCH_ID_PROD_6341,
+ 	[MV88E6XXX_FAMILY_6390] = MV88E6XXX_PORT_SWITCH_ID_PROD_6390,
++	[MV88E6XXX_FAMILY_6393] = MV88E6XXX_PORT_SWITCH_ID_PROD_6393X,
+ };
+ 
+ static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
+@@ -3071,7 +3141,10 @@
+ 
+ 	if (external) {
+ 		mv88e6xxx_reg_lock(chip);
+-		err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true);
++		if (chip->info->family == MV88E6XXX_FAMILY_6393)
++			err = mv88e6393x_g2_scratch_gpio_set_smi(chip, true);
++		else
++			err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true);
+ 		mv88e6xxx_reg_unlock(chip);
+ 
+ 		if (err)
+@@ -4505,6 +4578,68 @@
+ 	.phylink_validate = mv88e6390x_phylink_validate,
+ };
+ 
++static const struct mv88e6xxx_ops mv88e6393x_ops = {
++	/* MV88E6XXX_FAMILY_6393 */
++	.setup_errata = mv88e6393x_serdes_setup_errata,
++	.irl_init_all = mv88e6390_g2_irl_init_all,
++	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
++	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
++	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
++	.phy_read = mv88e6xxx_g2_smi_phy_read,
++	.phy_write = mv88e6xxx_g2_smi_phy_write,
++	.port_set_link = mv88e6xxx_port_set_link,
++	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
++	.port_set_speed_duplex = mv88e6393x_port_set_speed_duplex,
++	.port_max_speed_mode = mv88e6393x_port_max_speed_mode,
++	.port_tag_remap = mv88e6390_port_tag_remap,
++	.port_set_policy = mv88e6393x_port_set_policy,
++	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
++	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
++	.port_set_ether_type = mv88e6393x_port_set_ether_type,
++	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
++	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
++	.port_pause_limit = mv88e6390_port_pause_limit,
++	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
++	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
++	.port_get_cmode = mv88e6352_port_get_cmode,
++	.port_set_cmode = mv88e6393x_port_set_cmode,
++	.port_setup_message_port = mv88e6xxx_setup_message_port,
++	.port_set_upstream_port = mv88e6393x_port_set_upstream_port,
++	.stats_snapshot = mv88e6390_g1_stats_snapshot,
++	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
++	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
++	.stats_get_strings = mv88e6320_stats_get_strings,
++	.stats_get_stats = mv88e6390_stats_get_stats,
++	/* .set_cpu_port is missing because this family does not support a global
++	 * CPU port, only per port CPU port which is set via
++	 * .port_set_upstream_port method.
++	 */
++	.set_egress_port = mv88e6393x_set_egress_port,
++	.watchdog_ops = &mv88e6390_watchdog_ops,
++	.mgmt_rsvd2cpu = mv88e6393x_port_mgmt_rsvd2cpu,
++	.pot_clear = mv88e6xxx_g2_pot_clear,
++	.reset = mv88e6352_g1_reset,
++	.rmu_disable = mv88e6390_g1_rmu_disable,
++	.atu_get_hash = mv88e6165_g1_atu_get_hash,
++	.atu_set_hash = mv88e6165_g1_atu_set_hash,
++	.vtu_getnext = mv88e6390_g1_vtu_getnext,
++	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
++	.serdes_power = mv88e6393x_serdes_power,
++	.serdes_get_lane = mv88e6393x_serdes_get_lane,
++	.serdes_pcs_get_state = mv88e6393x_serdes_pcs_get_state,
++	.serdes_pcs_config = mv88e6390_serdes_pcs_config,
++	.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
++	.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
++	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
++	.serdes_irq_enable = mv88e6393x_serdes_irq_enable,
++	.serdes_irq_status = mv88e6393x_serdes_irq_status,
++	/* TODO: serdes stats */
++	.gpio_ops = &mv88e6352_gpio_ops,
++	.avb_ops = &mv88e6390_avb_ops,
++	.ptp_ops = &mv88e6352_ptp_ops,
++	.phylink_validate = mv88e6393x_phylink_validate,
++};
++
+ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
+ 	[MV88E6085] = {
+ 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6085,
+@@ -5166,6 +5301,28 @@
+ 		.ptp_support = true,
+ 		.ops = &mv88e6390x_ops,
+ 	},
++	[MV88E6393X] = {
++		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6393X,
++		.family = MV88E6XXX_FAMILY_6393,
++		.name = "Marvell 88E6393X",
++		.num_databases = 4096,
++		.num_ports = 11,	/* 10 + Z80 */
++		.num_internal_phys = 9,
++		.max_vid = 8191,
++		.port_base_addr = 0x0,
++		.phy_base_addr = 0x0,
++		.global1_addr = 0x1b,
++		.global2_addr = 0x1c,
++		.age_time_coeff = 3750,
++		.g1_irqs = 10,
++		.g2_irqs = 14,
++		.atu_move_port_mask = 0x1f,
++		.pvt = true,
++		.multi_chip = true,
++		.tag_protocol = DSA_TAG_PROTO_DSA,
++		.ptp_support = true,
++		.ops = &mv88e6393x_ops,
++	},
+ };
+ 
+ static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
+@@ -5354,6 +5511,11 @@
+ 	struct mv88e6xxx_chip *chip = ds->priv;
+ 	int err = -EOPNOTSUPP;
+ 
++	if (likely(chip->strict_cpu_mode)) {
++		if (!dsa_is_cpu_port(ds, port))
++			return 0;
++	}
++
+ 	mv88e6xxx_reg_lock(chip);
+ 	if (chip->info->ops->port_set_egress_floods)
+ 		err = chip->info->ops->port_set_egress_floods(chip, port,
+@@ -5420,6 +5582,17 @@
+ 	.devlink_info_get	= mv88e6xxx_devlink_info_get,
+ };
+ 
++static void mv88e6xxx_set_strict_cpu_mode(struct mv88e6xxx_chip *chip)
++{
++	struct dsa_switch *ds = chip->ds;
++	struct dsa_switch_tree *dst = ds->dst;
++	struct dsa_port *dp;
++	int val = chip->strict_cpu_mode;
++	list_for_each_entry(dp, &dst->ports, list) {
++		dp->strict_cpu_mode = val;
++	}
++}
++
+ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
+ {
+ 	struct device *dev = chip->dev;
+@@ -5436,7 +5609,10 @@
+ 	ds->ops = &mv88e6xxx_switch_ops;
+ 	ds->ageing_time_min = chip->info->age_time_coeff;
+ 	ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
+-	ds->assisted_learning_on_cpu_port = true;
++	if (likely(chip->strict_cpu_mode))
++		ds->assisted_learning_on_cpu_port = false;
++	else
++		ds->assisted_learning_on_cpu_port = true;
+ 
+ 	dev_set_drvdata(dev, ds);
+ 
+@@ -5517,6 +5693,13 @@
+ 		goto out;
+ 	}
+ 
++	chip->strict_cpu_mode = 0;
++	if (np) {
++		int strict_cpu_mode = 0;
++		if (!of_property_read_u32(np, "strict-cpu-mode", &strict_cpu_mode))
++			chip->strict_cpu_mode = strict_cpu_mode;
++	}
++
+ 	chip->info = compat_info;
+ 
+ 	err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
+@@ -5534,7 +5717,6 @@
+ 	err = mv88e6xxx_detect(chip);
+ 	if (err)
+ 		goto out;
+-
+ 	mv88e6xxx_phy_init(chip);
+ 
+ 	if (chip->info->ops->get_eeprom) {
+@@ -5544,7 +5726,6 @@
+ 		else
+ 			chip->eeprom_len = pdata->eeprom_len;
+ 	}
+-
+ 	mv88e6xxx_reg_lock(chip);
+ 	err = mv88e6xxx_switch_reset(chip);
+ 	mv88e6xxx_reg_unlock(chip);
+@@ -5598,6 +5779,8 @@
+ 	if (err)
+ 		goto out_mdio;
+ 
++	mv88e6xxx_set_strict_cpu_mode(chip);
++
+ 	return 0;
+ 
+ out_mdio:
+diff -rNu a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
+--- a/drivers/net/dsa/mv88e6xxx/chip.h	2022-01-16 10:14:24.000000000 +0200
++++ b/drivers/net/dsa/mv88e6xxx/chip.h	2022-01-19 12:01:32.590583314 +0200
+@@ -76,6 +76,7 @@
+ 	MV88E6352,
+ 	MV88E6390,
+ 	MV88E6390X,
++	MV88E6393X,
+ };
+ 
+ enum mv88e6xxx_family {
+@@ -91,6 +92,7 @@
+ 	MV88E6XXX_FAMILY_6351,	/* 6171 6175 6350 6351 */
+ 	MV88E6XXX_FAMILY_6352,	/* 6172 6176 6240 6352 */
+ 	MV88E6XXX_FAMILY_6390,  /* 6190 6190X 6191 6290 6390 6390X */
++	MV88E6XXX_FAMILY_6393,	/* 6191X 6193X 6393X */
+ };
+ 
+ struct mv88e6xxx_ops;
+@@ -321,6 +323,8 @@
+ 	struct kthread_worker *kworker;
+ 	struct kthread_delayed_work irq_poll_work;
+ 
++	bool strict_cpu_mode;
++
+ 	/* GPIO resources */
+ 	u8 gpio_data[2];
+ 
+@@ -507,30 +511,30 @@
+ 	int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip);
+ 
+ 	/* Power on/off a SERDES interface */
+-	int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, u8 lane,
++	int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, int lane,
+ 			    bool up);
+ 
+ 	/* SERDES lane mapping */
+-	u8 (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port);
++	int (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port);
+ 
+ 	int (*serdes_pcs_get_state)(struct mv88e6xxx_chip *chip, int port,
+-				    u8 lane, struct phylink_link_state *state);
++				    int lane, struct phylink_link_state *state);
+ 	int (*serdes_pcs_config)(struct mv88e6xxx_chip *chip, int port,
+-				 u8 lane, unsigned int mode,
++				 int lane, unsigned int mode,
+ 				 phy_interface_t interface,
+ 				 const unsigned long *advertise);
+ 	int (*serdes_pcs_an_restart)(struct mv88e6xxx_chip *chip, int port,
+-				     u8 lane);
++				     int lane);
+ 	int (*serdes_pcs_link_up)(struct mv88e6xxx_chip *chip, int port,
+-				  u8 lane, int speed, int duplex);
++				  int lane, int speed, int duplex);
+ 
+ 	/* SERDES interrupt handling */
+ 	unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip,
+ 					   int port);
+-	int (*serdes_irq_enable)(struct mv88e6xxx_chip *chip, int port, u8 lane,
++	int (*serdes_irq_enable)(struct mv88e6xxx_chip *chip, int port, int lane,
+ 				 bool enable);
+ 	irqreturn_t (*serdes_irq_status)(struct mv88e6xxx_chip *chip, int port,
+-					 u8 lane);
++					 int lane);
+ 
+ 	/* Statistics from the SERDES interface */
+ 	int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
+diff -rNu a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
+--- a/drivers/net/dsa/mv88e6xxx/global1.h	2022-01-16 10:14:24.000000000 +0200
++++ b/drivers/net/dsa/mv88e6xxx/global1.h	2022-01-17 07:57:18.909123339 +0200
+@@ -22,6 +22,7 @@
+ #define MV88E6185_G1_STS_PPU_STATE_DISABLED		0x8000
+ #define MV88E6185_G1_STS_PPU_STATE_POLLING		0xc000
+ #define MV88E6XXX_G1_STS_INIT_READY			0x0800
++#define MV88E6393X_G1_STS_IRQ_DEVICE_2			9
+ #define MV88E6XXX_G1_STS_IRQ_AVB			8
+ #define MV88E6XXX_G1_STS_IRQ_DEVICE			7
+ #define MV88E6XXX_G1_STS_IRQ_STATS			6
+@@ -59,6 +60,7 @@
+ #define MV88E6185_G1_CTL1_SCHED_PRIO		0x0800
+ #define MV88E6185_G1_CTL1_MAX_FRAME_1632	0x0400
+ #define MV88E6185_G1_CTL1_RELOAD_EEPROM		0x0200
++#define MV88E6393X_G1_CTL1_DEVICE2_EN		0x0200
+ #define MV88E6XXX_G1_CTL1_DEVICE_EN		0x0080
+ #define MV88E6XXX_G1_CTL1_STATS_DONE_EN		0x0040
+ #define MV88E6XXX_G1_CTL1_VTU_PROBLEM_EN	0x0020
+diff -rNu a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h
+--- a/drivers/net/dsa/mv88e6xxx/global2.h	2022-01-16 10:14:24.000000000 +0200
++++ b/drivers/net/dsa/mv88e6xxx/global2.h	2022-01-17 08:06:27.782014100 +0200
+@@ -38,9 +38,15 @@
+ /* Offset 0x02: MGMT Enable Register 2x */
+ #define MV88E6XXX_G2_MGMT_EN_2X		0x02
+ 
++/* Offset 0x02: MAC LINK change IRQ Register for MV88E6393X */
++#define MV88E6393X_G2_MACLINK_INT_SRC		0x02
++
+ /* Offset 0x03: MGMT Enable Register 0x */
+ #define MV88E6XXX_G2_MGMT_EN_0X		0x03
+ 
++/* Offset 0x03: MAC LINK change IRQ Mask Register for MV88E6393X */
++#define MV88E6393X_G2_MACLINK_INT_MASK		0x03
++
+ /* Offset 0x04: Flow Control Delay Register */
+ #define MV88E6XXX_G2_FLOW_CTL	0x04
+ 
+@@ -52,6 +58,8 @@
+ #define MV88E6XXX_G2_SWITCH_MGMT_FORCE_FLOW_CTL_PRI	0x0080
+ #define MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU		0x0008
+ 
++#define MV88E6393X_G2_EGRESS_MONITOR_DEST		0x05
++
+ /* Offset 0x06: Device Mapping Table Register */
+ #define MV88E6XXX_G2_DEVICE_MAPPING		0x06
+ #define MV88E6XXX_G2_DEVICE_MAPPING_UPDATE	0x8000
+@@ -362,6 +370,8 @@
+ 
+ int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
+ 				      bool external);
++int mv88e6393x_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
++				       bool external);
+ int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin);
+ int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats);
+ 
+diff -rNu a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
+--- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c	2022-01-16 10:14:24.000000000 +0200
++++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c	2022-01-17 07:30:35.109721197 +0200
+@@ -289,3 +289,34 @@
+ 
+ 	return mv88e6xxx_g2_scratch_write(chip, misc_cfg, val);
+ }
++
++/**
++ * mv88e6393x_g2_scratch_gpio_set_smi - set gpio muxing for external smi
++ * @chip: chip private data
++ * @external: set mux for external smi, or free for gpio usage
++ *
++ * MV88E6191X/6193X/6393X GPIO pins 9 and 10 can be configured as an
++ * external SMI interface or as regular GPIO-s.
++ *
++ * They however have a different register layout then the existing
++ * function.
++ */
++
++int mv88e6393x_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
++				       bool external)
++{
++	int misc_cfg = MV88E6352_G2_SCRATCH_MISC_CFG;
++	int err;
++	u8 val;
++
++	err = mv88e6xxx_g2_scratch_read(chip, misc_cfg, &val);
++	if (err)
++		return err;
++
++	if (external)
++		val &= ~MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI;
++	else
++		val |= MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI;
++
++	return mv88e6xxx_g2_scratch_write(chip, misc_cfg, val);
++}
+diff -rNu a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
+--- a/drivers/net/dsa/mv88e6xxx/port.c	2022-01-16 10:14:24.000000000 +0200
++++ b/drivers/net/dsa/mv88e6xxx/port.c	2022-01-20 08:11:56.312225774 +0200
+@@ -14,6 +14,7 @@
+ #include <linux/phylink.h>
+ 
+ #include "chip.h"
++#include "global2.h"
+ #include "port.h"
+ #include "serdes.h"
+ 
+@@ -25,6 +26,14 @@
+ 	return mv88e6xxx_read(chip, addr, reg, val);
+ }
+ 
++int mv88e6xxx_port_wait_bit(struct mv88e6xxx_chip *chip, int port, int reg,
++			    int bit, int val)
++{
++	int addr = chip->info->port_base_addr + port;
++
++	return mv88e6xxx_wait_bit(chip, addr, reg, bit, val);
++}
++
+ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
+ 			 u16 val)
+ {
+@@ -390,11 +399,111 @@
+ 	return PHY_INTERFACE_MODE_NA;
+ }
+ 
++/* Support 10, 100, 200, 1000, 2500, 5000, 10000 Mbps (e.g. 88E6393X)
++ * Function mv88e6xxx_port_set_speed_duplex() can't be used as the register
++ * values for speeds 2500 & 5000 conflict.
++ */
++int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
++				     int speed, int duplex)
++{
++	u16 reg, ctrl;
++	int err;
++
++	if (speed == SPEED_MAX)
++		speed = (port > 0 && port < 9) ? 1000 : 10000;
++
++	if (speed == 200 && port != 0)
++		return -EOPNOTSUPP;
++
++	if (speed >= 2500 && port > 0 && port < 9)
++		return -EOPNOTSUPP;
++
++	switch (speed) {
++	case 10:
++		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10;
++		break;
++	case 100:
++		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100;
++		break;
++	case 200:
++		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 |
++			MV88E6390_PORT_MAC_CTL_ALTSPEED;
++		break;
++	case 1000:
++		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
++		break;
++	case 2500:
++		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000 |
++			MV88E6390_PORT_MAC_CTL_ALTSPEED;
++		break;
++	case 5000:
++		ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
++			MV88E6390_PORT_MAC_CTL_ALTSPEED;
++		break;
++	case 10000:
++	case SPEED_UNFORCED:
++		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED;
++		break;
++	default:
++		return -EOPNOTSUPP;
++	}
++
++	switch (duplex) {
++	case DUPLEX_HALF:
++		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
++		break;
++	case DUPLEX_FULL:
++		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
++			MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
++		break;
++	case DUPLEX_UNFORCED:
++		/* normal duplex detection */
++		break;
++	default:
++		return -EOPNOTSUPP;
++	}
++
++	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
++	if (err)
++		return err;
++
++	reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK |
++		 MV88E6390_PORT_MAC_CTL_ALTSPEED |
++		 MV88E6390_PORT_MAC_CTL_FORCE_SPEED);
++
++	if (speed != SPEED_UNFORCED)
++		reg |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
++
++	reg |= ctrl;
++
++	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
++	if (err)
++		return err;
++
++	if (speed != SPEED_UNFORCED)
++		dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
++	else
++		dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
++	dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
++		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
++		reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");
++
++	return 0;
++}
++
++phy_interface_t mv88e6393x_port_max_speed_mode(int port)
++{
++	if (port == 0 || port == 9 || port == 10)
++		return PHY_INTERFACE_MODE_10GBASER;
++
++	return PHY_INTERFACE_MODE_NA;
++}
++
+ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
+ 				    phy_interface_t mode, bool force)
+ {
+-	u8 lane;
+ 	u16 cmode;
++	int lane;
+ 	u16 reg;
+ 	int err;
+ 
+@@ -421,6 +530,9 @@
+ 	case PHY_INTERFACE_MODE_RXAUI:
+ 		cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI;
+ 		break;
++	case PHY_INTERFACE_MODE_10GBASER:
++		cmode = MV88E6393X_PORT_STS_CMODE_10GBASER;
++		break;
+ 	default:
+ 		cmode = 0;
+ 	}
+@@ -430,7 +542,7 @@
+ 		return 0;
+ 
+ 	lane = mv88e6xxx_serdes_get_lane(chip, port);
+-	if (lane) {
++	if (lane >= 0) {
+ 		if (chip->ports[port].serdes_irq) {
+ 			err = mv88e6xxx_serdes_irq_disable(chip, port, lane);
+ 			if (err)
+@@ -459,8 +571,8 @@
+ 		chip->ports[port].cmode = cmode;
+ 
+ 		lane = mv88e6xxx_serdes_get_lane(chip, port);
+-		if (!lane)
+-			return -ENODEV;
++		if (lane < 0)
++			return lane;
+ 
+ 		err = mv88e6xxx_serdes_power_up(chip, port, lane);
+ 		if (err)
+@@ -505,6 +617,29 @@
+ 	return mv88e6xxx_port_set_cmode(chip, port, mode, false);
+ }
+ 
++int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
++			      phy_interface_t mode)
++{
++	int err;
++	u16 reg;
++
++	if (port != 0 && port != 9 && port != 10)
++		return -EOPNOTSUPP;
++
++	/* mv88e6393x errata 4.5: EEE should be disabled on SERDES ports */
++	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
++	if (err)
++		return err;
++
++	reg &= ~MV88E6XXX_PORT_MAC_CTL_EEE;
++	reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_EEE;
++	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
++	if (err)
++		return err;
++
++	return mv88e6xxx_port_set_cmode(chip, port, mode, false);
++}
++
+ static int mv88e6341_port_set_cmode_writable(struct mv88e6xxx_chip *chip,
+ 					     int port)
+ {
+@@ -1071,7 +1206,16 @@
+ 	if (err)
+ 		return err;
+ 
+-	reg |= MV88E6XXX_PORT_CTL2_MAP_DA;
++	if (likely(chip->strict_cpu_mode)) {
++		/*  When this bit is set to a one, normal switch operation occurs where
++				a frame’s DA is used to direct the frame out of the correct ports.
++				When this bit is cleared to a zero, the frame will be sent out of the ports
++				defined by EgressFloods (port offset 0x04) even if the DA is found in the
++				address database. */
++		reg &= ~MV88E6XXX_PORT_CTL2_MAP_DA;
++	} else {
++		reg |= MV88E6XXX_PORT_CTL2_MAP_DA;
++	}
+ 
+ 	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
+ }
+@@ -1130,6 +1274,155 @@
+ 	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 0);
+ }
+ 
++/* Offset 0x0E: Policy & MGMT Control Register for FAMILY 6191X 6193X 6393X */
++
++static int mv88e6393x_port_policy_read(struct mv88e6xxx_chip *chip, int port,
++				       u16 pointer, u8 *data)
++{
++	u16 reg;
++	int err;
++
++	err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
++				   pointer);
++	if (err)
++		return err;
++
++	err = mv88e6xxx_port_read(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
++				  &reg);
++	if (err)
++		return err;
++
++	*data = reg;
++
++	return 0;
++}
++
++static int mv88e6393x_port_policy_write(struct mv88e6xxx_chip *chip, int port,
++					u16 pointer, u8 data)
++{
++	u16 reg;
++
++	reg = MV88E6393X_PORT_POLICY_MGMT_CTL_UPDATE | pointer | data;
++
++	return mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
++				    reg);
++}
++
++static int mv88e6393x_port_policy_write_all(struct mv88e6xxx_chip *chip,
++					    u16 pointer, u8 data)
++{
++	int err, port;
++
++	for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
++		if (dsa_is_unused_port(chip->ds, port))
++			continue;
++
++		err = mv88e6393x_port_policy_write(chip, port, pointer, data);
++		if (err)
++			return err;
++	}
++
++	return 0;
++}
++
++int mv88e6393x_set_egress_port(struct mv88e6xxx_chip *chip,
++			       enum mv88e6xxx_egress_direction direction,
++			       int port)
++{
++	u16 ptr;
++	int err;
++
++	switch (direction) {
++	case MV88E6XXX_EGRESS_DIR_INGRESS:
++		ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_INGRESS_DEST;
++		err = mv88e6393x_port_policy_write_all(chip, ptr, port);
++		if (err)
++			return err;
++		break;
++	case MV88E6XXX_EGRESS_DIR_EGRESS:
++		ptr = MV88E6393X_G2_EGRESS_MONITOR_DEST;
++		err = mv88e6xxx_g2_write(chip, ptr, port);
++		if (err)
++			return err;
++		break;
++	}
++
++	return 0;
++}
++
++int mv88e6393x_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
++				      int upstream_port)
++{
++	u16 ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_CPU_DEST;
++	u8 data = MV88E6393X_PORT_POLICY_MGMT_CTL_CPU_DEST_MGMTPRI |
++		  upstream_port;
++	return mv88e6393x_port_policy_write(chip, port, ptr, data);
++}
++
++int mv88e6393x_port_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
++{
++	u16 ptr;
++	int err;
++
++	/* Consider the frames with reserved multicast destination
++	 * addresses matching 01:80:c2:00:00:00 and
++	 * 01:80:c2:00:00:02 as MGMT.
++	 */
++	ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XLO;
++	err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
++	if (err)
++		return err;
++
++	ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XHI;
++	err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
++	if (err)
++		return err;
++
++	ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XLO;
++	err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
++	if (err)
++		return err;
++
++	ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XHI;
++	err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
++	if (err)
++		return err;
++
++	return 0;
++}
++
++/* Offset 0x10 & 0x11: EPC */
++
++static int mv88e6393x_port_epc_wait_ready(struct mv88e6xxx_chip *chip, int port)
++{
++	int bit = __bf_shf(MV88E6393X_PORT_EPC_CMD_BUSY);
++
++	return mv88e6xxx_port_wait_bit(chip, port, MV88E6393X_PORT_EPC_CMD, bit, 0);
++}
++
++/* Port Ether type for 6393X family */
++
++int mv88e6393x_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
++				   u16 etype)
++{
++	u16 val;
++	int err;
++
++	err = mv88e6393x_port_epc_wait_ready(chip, port);
++	if (err)
++		return err;
++
++	err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_DATA, etype);
++	if (err)
++		return err;
++
++	val = MV88E6393X_PORT_EPC_CMD_BUSY |
++	      MV88E6393X_PORT_EPC_CMD_WRITE |
++	      MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE;
++
++	return mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_CMD, val);
++}
++
+ /* Offset 0x0f: Port Ether type */
+ 
+ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
+@@ -1204,46 +1497,43 @@
+ 
+ /* Offset 0x0E: Policy Control Register */
+ 
+-int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
+-			      enum mv88e6xxx_policy_mapping mapping,
+-			      enum mv88e6xxx_policy_action action)
++static int
++mv88e6xxx_port_policy_mapping_get_pos(enum mv88e6xxx_policy_mapping mapping,
++				      enum mv88e6xxx_policy_action action,
++				      u16 *mask, u16 *val, int *shift)
+ {
+-	u16 reg, mask, val;
+-	int shift;
+-	int err;
+-
+ 	switch (mapping) {
+ 	case MV88E6XXX_POLICY_MAPPING_DA:
+-		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_DA_MASK);
+-		mask = MV88E6XXX_PORT_POLICY_CTL_DA_MASK;
++		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_DA_MASK);
++		*mask = MV88E6XXX_PORT_POLICY_CTL_DA_MASK;
+ 		break;
+ 	case MV88E6XXX_POLICY_MAPPING_SA:
+-		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_SA_MASK);
+-		mask = MV88E6XXX_PORT_POLICY_CTL_SA_MASK;
++		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_SA_MASK);
++		*mask = MV88E6XXX_PORT_POLICY_CTL_SA_MASK;
+ 		break;
+ 	case MV88E6XXX_POLICY_MAPPING_VTU:
+-		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VTU_MASK);
+-		mask = MV88E6XXX_PORT_POLICY_CTL_VTU_MASK;
++		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VTU_MASK);
++		*mask = MV88E6XXX_PORT_POLICY_CTL_VTU_MASK;
+ 		break;
+ 	case MV88E6XXX_POLICY_MAPPING_ETYPE:
+-		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK);
+-		mask = MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK;
++		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK);
++		*mask = MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK;
+ 		break;
+ 	case MV88E6XXX_POLICY_MAPPING_PPPOE:
+-		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK);
+-		mask = MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK;
++		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK);
++		*mask = MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK;
+ 		break;
+ 	case MV88E6XXX_POLICY_MAPPING_VBAS:
+-		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK);
+-		mask = MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK;
++		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK);
++		*mask = MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK;
+ 		break;
+ 	case MV88E6XXX_POLICY_MAPPING_OPT82:
+-		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK);
+-		mask = MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK;
++		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK);
++		*mask = MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK;
+ 		break;
+ 	case MV88E6XXX_POLICY_MAPPING_UDP:
+-		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_UDP_MASK);
+-		mask = MV88E6XXX_PORT_POLICY_CTL_UDP_MASK;
++		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_UDP_MASK);
++		*mask = MV88E6XXX_PORT_POLICY_CTL_UDP_MASK;
+ 		break;
+ 	default:
+ 		return -EOPNOTSUPP;
+@@ -1251,21 +1541,39 @@
+ 
+ 	switch (action) {
+ 	case MV88E6XXX_POLICY_ACTION_NORMAL:
+-		val = MV88E6XXX_PORT_POLICY_CTL_NORMAL;
++		*val = MV88E6XXX_PORT_POLICY_CTL_NORMAL;
+ 		break;
+ 	case MV88E6XXX_POLICY_ACTION_MIRROR:
+-		val = MV88E6XXX_PORT_POLICY_CTL_MIRROR;
++		*val = MV88E6XXX_PORT_POLICY_CTL_MIRROR;
+ 		break;
+ 	case MV88E6XXX_POLICY_ACTION_TRAP:
+-		val = MV88E6XXX_PORT_POLICY_CTL_TRAP;
++		*val = MV88E6XXX_PORT_POLICY_CTL_TRAP;
+ 		break;
+ 	case MV88E6XXX_POLICY_ACTION_DISCARD:
+-		val = MV88E6XXX_PORT_POLICY_CTL_DISCARD;
++		*val = MV88E6XXX_PORT_POLICY_CTL_DISCARD;
+ 		break;
+ 	default:
+ 		return -EOPNOTSUPP;
+ 	}
+ 
++	return 0;
++}
++
++/* Offset 0x0E: Policy Control Register */
++
++int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
++			      enum mv88e6xxx_policy_mapping mapping,
++			      enum mv88e6xxx_policy_action action)
++{
++	u16 reg, mask, val;
++	int shift;
++	int err;
++
++	err = mv88e6xxx_port_policy_mapping_get_pos(mapping, action, &mask,
++						    &val, &shift);
++	if (err)
++		return err;
++
+ 	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_POLICY_CTL, &reg);
+ 	if (err)
+ 		return err;
+@@ -1275,3 +1583,37 @@
+ 
+ 	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_POLICY_CTL, reg);
+ }
++
++int mv88e6393x_port_set_policy(struct mv88e6xxx_chip *chip, int port,
++			       enum mv88e6xxx_policy_mapping mapping,
++			       enum mv88e6xxx_policy_action action)
++{
++	u16 mask, val;
++	int shift;
++	int err;
++	u16 ptr;
++	u8 reg;
++
++	err = mv88e6xxx_port_policy_mapping_get_pos(mapping, action, &mask,
++						    &val, &shift);
++	if (err)
++		return err;
++
++	/* The 16-bit Port Policy CTL register from older chips is on 6393x
++	 * changed to Port Policy MGMT CTL, which can access more data, but
++	 * indirectly. The original 16-bit value is divided into two 8-bit
++	 * registers.
++	 */
++	ptr = shift / 8;
++	shift %= 8;
++	mask >>= ptr * 8;
++
++	err = mv88e6393x_port_policy_read(chip, port, ptr, &reg);
++	if (err)
++		return err;
++
++	reg &= ~mask;
++	reg |= (val << shift) & mask;
++
++	return mv88e6393x_port_policy_write(chip, port, ptr, reg);
++}
+diff -rNu a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
+--- a/drivers/net/dsa/mv88e6xxx/port.h	2022-01-16 10:14:24.000000000 +0200
++++ b/drivers/net/dsa/mv88e6xxx/port.h	2022-01-19 06:15:06.110639591 +0200
+@@ -49,6 +49,9 @@
+ #define MV88E6XXX_PORT_STS_CMODE_2500BASEX	0x000b
+ #define MV88E6XXX_PORT_STS_CMODE_XAUI		0x000c
+ #define MV88E6XXX_PORT_STS_CMODE_RXAUI		0x000d
++#define MV88E6393X_PORT_STS_CMODE_5GBASER	0x000c
++#define MV88E6393X_PORT_STS_CMODE_10GBASER	0x000d
++#define MV88E6393X_PORT_STS_CMODE_USXGMII	0x000e
+ #define MV88E6185_PORT_STS_CDUPLEX		0x0008
+ #define MV88E6185_PORT_STS_CMODE_MASK		0x0007
+ #define MV88E6185_PORT_STS_CMODE_GMII_FD	0x0000
+@@ -68,6 +71,8 @@
+ #define MV88E6390_PORT_MAC_CTL_FORCE_SPEED		0x2000
+ #define MV88E6390_PORT_MAC_CTL_ALTSPEED			0x1000
+ #define MV88E6352_PORT_MAC_CTL_200BASE			0x1000
++#define MV88E6XXX_PORT_MAC_CTL_EEE			0x0200
++#define MV88E6XXX_PORT_MAC_CTL_FORCE_EEE		0x0100
+ #define MV88E6185_PORT_MAC_CTL_AN_EN			0x0400
+ #define MV88E6185_PORT_MAC_CTL_AN_RESTART		0x0200
+ #define MV88E6185_PORT_MAC_CTL_AN_DONE			0x0100
+@@ -117,6 +122,8 @@
+ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6176	0x1760
+ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6190	0x1900
+ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6191	0x1910
++#define MV88E6XXX_PORT_SWITCH_ID_PROD_6191X	0x1920
++#define MV88E6XXX_PORT_SWITCH_ID_PROD_6193X	0x1930
+ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6185	0x1a70
+ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6220	0x2200
+ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240	0x2400
+@@ -129,6 +136,7 @@
+ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6350	0x3710
+ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6351	0x3750
+ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6390	0x3900
++#define MV88E6XXX_PORT_SWITCH_ID_PROD_6393X	0x3930
+ #define MV88E6XXX_PORT_SWITCH_ID_REV_MASK	0x000f
+ 
+ /* Offset 0x04: Port Control Register */
+@@ -173,6 +181,7 @@
+ /* Offset 0x06: Port Based VLAN Map */
+ #define MV88E6XXX_PORT_BASE_VLAN		0x06
+ #define MV88E6XXX_PORT_BASE_VLAN_FID_3_0_MASK	0xf000
++#define MV88E6XXX_PORT_BASE_VLAN_LEARN_DISABLE 0x800
+ 
+ /* Offset 0x07: Default Port VLAN ID & Priority */
+ #define MV88E6XXX_PORT_DEFAULT_VLAN		0x07
+@@ -236,6 +245,19 @@
+ #define MV88E6XXX_PORT_POLICY_CTL_TRAP		0x0002
+ #define MV88E6XXX_PORT_POLICY_CTL_DISCARD	0x0003
+ 
++/* Offset 0x0E: Policy & MGMT Control Register (FAMILY_6393X) */
++#define MV88E6393X_PORT_POLICY_MGMT_CTL				0x0e
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_UPDATE			0x8000
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_MASK		0x3f00
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_DATA_MASK		0x00ff
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XLO	0x2000
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XHI	0x2100
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XLO	0x2400
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XHI	0x2500
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_INGRESS_DEST	0x3000
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_CPU_DEST		0x3800
++#define MV88E6393X_PORT_POLICY_MGMT_CTL_CPU_DEST_MGMTPRI	0x00e0
++
+ /* Offset 0x0F: Port Special Ether Type */
+ #define MV88E6XXX_PORT_ETH_TYPE		0x0f
+ #define MV88E6XXX_PORT_ETH_TYPE_DEFAULT	0x9100
+@@ -243,6 +265,15 @@
+ /* Offset 0x10: InDiscards Low Counter */
+ #define MV88E6XXX_PORT_IN_DISCARD_LO	0x10
+ 
++/* Offset 0x10: Extended Port Control Command */
++#define MV88E6393X_PORT_EPC_CMD		0x10
++#define MV88E6393X_PORT_EPC_CMD_BUSY	0x8000
++#define MV88E6393X_PORT_EPC_CMD_WRITE	0x0300
++#define MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE	0x02
++
++/* Offset 0x11: Extended Port Control Data */
++#define MV88E6393X_PORT_EPC_DATA	0x11
++
+ /* Offset 0x11: InDiscards High Counter */
+ #define MV88E6XXX_PORT_IN_DISCARD_HI	0x11
+ 
+@@ -312,10 +343,13 @@
+ 				    int speed, int duplex);
+ int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
+ 				     int speed, int duplex);
++int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
++				     int speed, int duplex);
+ 
+ phy_interface_t mv88e6341_port_max_speed_mode(int port);
+ phy_interface_t mv88e6390_port_max_speed_mode(int port);
+ phy_interface_t mv88e6390x_port_max_speed_mode(int port);
++phy_interface_t mv88e6393x_port_max_speed_mode(int port);
+ 
+ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);
+ 
+@@ -344,8 +378,19 @@
+ int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
+ 			      enum mv88e6xxx_policy_mapping mapping,
+ 			      enum mv88e6xxx_policy_action action);
++int mv88e6393x_port_set_policy(struct mv88e6xxx_chip *chip, int port,
++			       enum mv88e6xxx_policy_mapping mapping,
++			       enum mv88e6xxx_policy_action action);
+ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
+ 				  u16 etype);
++int mv88e6393x_set_egress_port(struct mv88e6xxx_chip *chip,
++			       enum mv88e6xxx_egress_direction direction,
++			       int port);
++int mv88e6393x_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
++				      int upstream_port);
++int mv88e6393x_port_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
++int mv88e6393x_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
++				   u16 etype);
+ int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
+ 				    bool message_port);
+ int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
+@@ -362,6 +407,8 @@
+ 			     phy_interface_t mode);
+ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
+ 			      phy_interface_t mode);
++int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
++			      phy_interface_t mode);
+ int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
+ int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
+ int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
+diff -rNu a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c
+--- a/drivers/net/dsa/mv88e6xxx/serdes.c	2022-01-16 10:14:24.000000000 +0200
++++ b/drivers/net/dsa/mv88e6xxx/serdes.c	2022-01-17 15:42:41.847693934 +0200
+@@ -95,7 +95,7 @@
+ 	return 0;
+ }
+ 
+-int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
++int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
+ 			   bool up)
+ {
+ 	u16 val, new_val;
+@@ -117,7 +117,7 @@
+ }
+ 
+ int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
+-				u8 lane, unsigned int mode,
++				int lane, unsigned int mode,
+ 				phy_interface_t interface,
+ 				const unsigned long *advertise)
+ {
+@@ -166,7 +166,7 @@
+ }
+ 
+ int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
+-				   u8 lane, struct phylink_link_state *state)
++				   int lane, struct phylink_link_state *state)
+ {
+ 	u16 lpa, status;
+ 	int err;
+@@ -187,7 +187,7 @@
+ }
+ 
+ int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
+-				    u8 lane)
++				    int lane)
+ {
+ 	u16 bmcr;
+ 	int err;
+@@ -200,7 +200,7 @@
+ }
+ 
+ int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
+-				 u8 lane, int speed, int duplex)
++				 int lane, int speed, int duplex)
+ {
+ 	u16 val, bmcr;
+ 	int err;
+@@ -230,10 +230,10 @@
+ 	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
+ }
+ 
+-u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
++int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
+ {
+ 	u8 cmode = chip->ports[port].cmode;
+-	u8 lane = 0;
++	int lane = -ENODEV;
+ 
+ 	if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX) ||
+ 	    (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX) ||
+@@ -245,7 +245,7 @@
+ 
+ static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
+ {
+-	if (mv88e6xxx_serdes_get_lane(chip, port))
++	if (mv88e6xxx_serdes_get_lane(chip, port) >= 0)
+ 		return true;
+ 
+ 	return false;
+@@ -354,7 +354,7 @@
+ }
+ 
+ irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
+-					u8 lane)
++					int lane)
+ {
+ 	irqreturn_t ret = IRQ_NONE;
+ 	u16 status;
+@@ -372,7 +372,7 @@
+ 	return ret;
+ }
+ 
+-int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
++int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
+ 				bool enable)
+ {
+ 	u16 val = 0;
+@@ -411,10 +411,10 @@
+ 	}
+ }
+ 
+-u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
++int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
+ {
+ 	u8 cmode = chip->ports[port].cmode;
+-	u8 lane = 0;
++	int lane = -ENODEV;
+ 
+ 	switch (port) {
+ 	case 5:
+@@ -428,10 +428,10 @@
+ 	return lane;
+ }
+ 
+-u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
++int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
+ {
+ 	u8 cmode = chip->ports[port].cmode;
+-	u8 lane = 0;
++	int lane = -ENODEV;
+ 
+ 	switch (port) {
+ 	case 9:
+@@ -451,12 +451,12 @@
+ 	return lane;
+ }
+ 
+-u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
++int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
+ {
+ 	u8 cmode_port = chip->ports[port].cmode;
+ 	u8 cmode_port10 = chip->ports[10].cmode;
+ 	u8 cmode_port9 = chip->ports[9].cmode;
+-	u8 lane = 0;
++	int lane = -ENODEV;
+ 
+ 	switch (port) {
+ 	case 2:
+@@ -526,8 +526,29 @@
+ 	return lane;
+ }
+ 
++/* Only Ports 0, 9 and 10 have SERDES lanes. Return the SERDES lane address
++ * a port is using else Returns -ENODEV.
++ */
++int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
++{
++	u8 cmode = chip->ports[port].cmode;
++	int lane = -ENODEV;
++
++	if (port != 0 && port != 9 && port != 10)
++		return -EOPNOTSUPP;
++
++	if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
++	    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
++	    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
++	    cmode == MV88E6393X_PORT_STS_CMODE_5GBASER ||
++	    cmode == MV88E6393X_PORT_STS_CMODE_10GBASER)
++		lane = port;
++
++	return lane;
++}
++
+ /* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */
+-static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane,
++static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane,
+ 				      bool up)
+ {
+ 	u16 val, new_val;
+@@ -554,7 +575,7 @@
+ }
+ 
+ /* Set power up/down for SGMII and 1000Base-X */
+-static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane,
++static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane,
+ 					bool up)
+ {
+ 	u16 val, new_val;
+@@ -590,7 +611,7 @@
+ 
+ int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
+ {
+-	if (mv88e6xxx_serdes_get_lane(chip, port) == 0)
++	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
+ 		return 0;
+ 
+ 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
+@@ -602,7 +623,7 @@
+ 	struct mv88e6390_serdes_hw_stat *stat;
+ 	int i;
+ 
+-	if (mv88e6xxx_serdes_get_lane(chip, port) == 0)
++	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
+ 		return 0;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
+@@ -639,7 +660,7 @@
+ 	int i;
+ 
+ 	lane = mv88e6xxx_serdes_get_lane(chip, port);
+-	if (lane == 0)
++	if (lane < 0)
+ 		return 0;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
+@@ -650,7 +671,7 @@
+ 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
+ }
+ 
+-static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, u8 lane)
++static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, int lane)
+ {
+ 	u16 reg;
+ 	int err;
+@@ -665,7 +686,7 @@
+ 				      MV88E6390_PG_CONTROL, reg);
+ }
+ 
+-int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
++int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
+ 			   bool up)
+ {
+ 	u8 cmode = chip->ports[port].cmode;
+@@ -690,7 +711,7 @@
+ }
+ 
+ int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
+-				u8 lane, unsigned int mode,
++				int lane, unsigned int mode,
+ 				phy_interface_t interface,
+ 				const unsigned long *advertise)
+ {
+@@ -749,7 +770,7 @@
+ }
+ 
+ static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
+-	int port, u8 lane, struct phylink_link_state *state)
++	int port, int lane, struct phylink_link_state *state)
+ {
+ 	u16 lpa, status;
+ 	int err;
+@@ -772,13 +793,35 @@
+ }
+ 
+ static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
+-	int port, u8 lane, struct phylink_link_state *state)
++	int port, int lane, struct phylink_link_state *state)
++{
++	u16 status;
++	int err;
++
++	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++				    MV88E6390_10G_STAT1, &status);
++	if (err)
++		return err;
++
++	state->link = !!(status & MDIO_STAT1_LSTATUS);
++	if (state->link) {
++		state->speed = SPEED_10000;
++		state->duplex = DUPLEX_FULL;
++	}
++
++	return 0;
++}
++
++static int mv88e6393x_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
++					       int port, int lane,
++					       struct phylink_link_state *state)
+ {
+ 	u16 status;
+ 	int err;
+ 
+ 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+ 				    MV88E6390_10G_STAT1, &status);
++
+ 	if (err)
+ 		return err;
+ 
+@@ -792,7 +835,7 @@
+ }
+ 
+ int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
+-				   u8 lane, struct phylink_link_state *state)
++				   int lane, struct phylink_link_state *state)
+ {
+ 	switch (state->interface) {
+ 	case PHY_INTERFACE_MODE_SGMII:
+@@ -810,8 +853,26 @@
+ 	}
+ }
+ 
++int mv88e6393x_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
++				    int lane, struct phylink_link_state *state)
++{
++	switch (state->interface) {
++	case PHY_INTERFACE_MODE_SGMII:
++	case PHY_INTERFACE_MODE_1000BASEX:
++	case PHY_INTERFACE_MODE_2500BASEX:
++		return mv88e6390_serdes_pcs_get_state_sgmii(chip, port, lane,
++							    state);
++	case PHY_INTERFACE_MODE_10GBASER:
++		return mv88e6393x_serdes_pcs_get_state_10g(chip, port, lane,
++							   state);
++
++	default:
++		return -EOPNOTSUPP;
++	}
++}
++
+ int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
+-				    u8 lane)
++				    int lane)
+ {
+ 	u16 bmcr;
+ 	int err;
+@@ -827,7 +888,7 @@
+ }
+ 
+ int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
+-				 u8 lane, int speed, int duplex)
++				 int lane, int speed, int duplex)
+ {
+ 	u16 val, bmcr;
+ 	int err;
+@@ -861,7 +922,7 @@
+ }
+ 
+ static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
+-					    int port, u8 lane)
++					    int port, int lane)
+ {
+ 	u16 bmsr;
+ 	int err;
+@@ -877,8 +938,25 @@
+ 	dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));
+ }
+ 
++static void mv88e6393x_serdes_irq_link_10g(struct mv88e6xxx_chip *chip,
++					   int port, int lane)
++{
++	u16 status;
++	int err;
++
++	/* If the link has dropped, we want to know about it. */
++	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++				    MV88E6390_10G_STAT1, &status);
++	if (err) {
++		dev_err(chip->dev, "can't read Serdes STAT1: %d\n", err);
++		return;
++	}
++
++	dsa_port_phylink_mac_change(chip->ds, port, !!(status & MDIO_STAT1_LSTATUS));
++}
++
+ static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
+-					     u8 lane, bool enable)
++					     int lane, bool enable)
+ {
+ 	u16 val = 0;
+ 
+@@ -890,7 +968,7 @@
+ 				      MV88E6390_SGMII_INT_ENABLE, val);
+ }
+ 
+-int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
++int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
+ 				bool enable)
+ {
+ 	u8 cmode = chip->ports[port].cmode;
+@@ -906,7 +984,7 @@
+ }
+ 
+ static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
+-					     u8 lane, u16 *status)
++					     int lane, u16 *status)
+ {
+ 	int err;
+ 
+@@ -916,8 +994,85 @@
+ 	return err;
+ }
+ 
++static int mv88e6393x_serdes_irq_enable_10g(struct mv88e6xxx_chip *chip,
++					    int lane, bool enable)
++{
++	u16 val = 0;
++
++	if (enable)
++		val |= MV88E6393X_10G_INT_LINK_CHANGE;
++
++	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
++				      MV88E6393X_10G_INT_ENABLE, val);
++}
++
++int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
++				 int lane, bool enable)
++{
++	u8 cmode = chip->ports[port].cmode;
++
++	switch (cmode) {
++	case MV88E6XXX_PORT_STS_CMODE_SGMII:
++	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
++	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
++		return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable);
++	case MV88E6393X_PORT_STS_CMODE_5GBASER:
++	case MV88E6393X_PORT_STS_CMODE_10GBASER:
++		return mv88e6393x_serdes_irq_enable_10g(chip, lane, enable);
++	}
++
++	return 0;
++}
++
++static int mv88e6393x_serdes_irq_status_10g(struct mv88e6xxx_chip *chip,
++					    int lane, u16 *status)
++{
++	int err;
++
++	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++				    MV88E6393X_10G_INT_STATUS, status);
++
++	return err;
++}
++
++irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
++					 int lane)
++{
++	u8 cmode = chip->ports[port].cmode;
++	irqreturn_t ret = IRQ_NONE;
++	u16 status;
++	int err;
++
++	switch (cmode) {
++	case MV88E6XXX_PORT_STS_CMODE_SGMII:
++	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
++	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
++		err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
++		if (err)
++			return ret;
++		if (status & (MV88E6390_SGMII_INT_LINK_DOWN |
++			      MV88E6390_SGMII_INT_LINK_UP)) {
++			ret = IRQ_HANDLED;
++			mv88e6390_serdes_irq_link_sgmii(chip, port, lane);
++		}
++		break;
++	case MV88E6393X_PORT_STS_CMODE_5GBASER:
++	case MV88E6393X_PORT_STS_CMODE_10GBASER:
++		err = mv88e6393x_serdes_irq_status_10g(chip, lane, &status);
++		if (err)
++			return err;
++		if (status & MV88E6393X_10G_INT_LINK_CHANGE) {
++			ret = IRQ_HANDLED;
++			mv88e6393x_serdes_irq_link_10g(chip, port, lane);
++		}
++		break;
++	}
++
++	return ret;
++}
++
+ irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
+-					u8 lane)
++					int lane)
+ {
+ 	u8 cmode = chip->ports[port].cmode;
+ 	irqreturn_t ret = IRQ_NONE;
+@@ -976,7 +1131,7 @@
+ 
+ int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
+ {
+-	if (mv88e6xxx_serdes_get_lane(chip, port) == 0)
++	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
+ 		return 0;
+ 
+ 	return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16);
+@@ -987,15 +1142,271 @@
+ 	u16 *p = _p;
+ 	int lane;
+ 	u16 reg;
++	int err;
+ 	int i;
+ 
+ 	lane = mv88e6xxx_serdes_get_lane(chip, port);
+-	if (lane == 0)
++	if (lane < 0)
+ 		return;
+ 
+ 	for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) {
+-		mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+-				      mv88e6390_serdes_regs[i], &reg);
+-		p[i] = reg;
++		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++					    mv88e6390_serdes_regs[i], &reg);
++		if (!err)
++			p[i] = reg;
+ 	}
+ }
++
++static int mv88e6393x_serdes_power_lane(struct mv88e6xxx_chip *chip, int lane,
++					bool on)
++{
++	u16 reg;
++	int err;
++
++	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++				    MV88E6393X_SERDES_CTRL1, &reg);
++	if (err)
++		return err;
++
++	if (on)
++		reg &= ~(MV88E6393X_SERDES_CTRL1_TX_PDOWN |
++			 MV88E6393X_SERDES_CTRL1_RX_PDOWN);
++	else
++		reg |= MV88E6393X_SERDES_CTRL1_TX_PDOWN |
++		       MV88E6393X_SERDES_CTRL1_RX_PDOWN;
++
++	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
++				      MV88E6393X_SERDES_CTRL1, reg);
++}
++
++static int mv88e6393x_serdes_erratum_4_6(struct mv88e6xxx_chip *chip, int lane)
++{
++	u16 reg;
++	int err;
++
++	/* mv88e6393x family errata 4.6:
++	 * Cannot clear PwrDn bit on SERDES if device is configured CPU_MGD
++	 * mode or P0_mode is configured for [x]MII.
++	 * Workaround: Set SERDES register 4.F002 bit 5=0 and bit 15=1.
++	 *
++	 * It seems that after this workaround the SERDES is automatically
++	 * powered up (the bit is cleared), so power it down.
++	 */
++	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++				    MV88E6393X_SERDES_POC, &reg);
++	if (err)
++		return err;
++
++	reg &= ~MV88E6393X_SERDES_POC_PDOWN;
++	reg |= MV88E6393X_SERDES_POC_RESET;
++
++	err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
++				     MV88E6393X_SERDES_POC, reg);
++	if (err)
++		return err;
++
++	err = mv88e6390_serdes_power_sgmii(chip, lane, false);
++	if (err)
++		return err;
++
++	return mv88e6393x_serdes_power_lane(chip, lane, false);
++}
++
++int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip)
++{
++	int err;
++
++	err = mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT0_LANE);
++	if (err)
++		return err;
++
++	err = mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT9_LANE);
++	if (err)
++		return err;
++
++	return mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT10_LANE);
++}
++
++static int mv88e6393x_serdes_erratum_4_8(struct mv88e6xxx_chip *chip, int lane)
++{
++	u16 reg, pcs;
++	int err;
++
++	/* mv88e6393x family errata 4.8:
++	 * When a SERDES port is operating in 1000BASE-X or SGMII mode link may
++	 * not come up after hardware reset or software reset of SERDES core.
++	 * Workaround is to write SERDES register 4.F074.14=1 for only those
++	 * modes and 0 in all other modes.
++	 */
++	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++				    MV88E6393X_SERDES_POC, &pcs);
++	if (err)
++		return err;
++
++	pcs &= MV88E6393X_SERDES_POC_PCS_MASK;
++
++	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++				    MV88E6393X_ERRATA_4_8_REG, &reg);
++	if (err)
++		return err;
++
++	if (pcs == MV88E6393X_SERDES_POC_PCS_1000BASEX ||
++	    pcs == MV88E6393X_SERDES_POC_PCS_SGMII_PHY ||
++	    pcs == MV88E6393X_SERDES_POC_PCS_SGMII_MAC)
++		reg |= MV88E6393X_ERRATA_4_8_BIT;
++	else
++		reg &= ~MV88E6393X_ERRATA_4_8_BIT;
++
++	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
++				      MV88E6393X_ERRATA_4_8_REG, reg);
++}
++
++static int mv88e6393x_serdes_erratum_5_2(struct mv88e6xxx_chip *chip, int lane,
++					 u8 cmode)
++{
++	static const struct {
++		u16 dev, reg, val, mask;
++	} fixes[] = {
++		{ MDIO_MMD_VEND1, 0x8093, 0xcb5a, 0xffff },
++		{ MDIO_MMD_VEND1, 0x8171, 0x7088, 0xffff },
++		{ MDIO_MMD_VEND1, 0x80c9, 0x311a, 0xffff },
++		{ MDIO_MMD_VEND1, 0x80a2, 0x8000, 0xff7f },
++		{ MDIO_MMD_VEND1, 0x80a9, 0x0000, 0xfff0 },
++		{ MDIO_MMD_VEND1, 0x80a3, 0x0000, 0xf8ff },
++		{ MDIO_MMD_PHYXS, MV88E6393X_SERDES_POC,
++		  MV88E6393X_SERDES_POC_RESET, MV88E6393X_SERDES_POC_RESET },
++	};
++	int err, i;
++	u16 reg;
++
++	/* mv88e6393x family errata 5.2:
++	 * For optimal signal integrity the following sequence should be applied
++	 * to SERDES operating in 10G mode. These registers only apply to 10G
++	 * operation and have no effect on other speeds.
++	 */
++	if (cmode != MV88E6393X_PORT_STS_CMODE_10GBASER)
++		return 0;
++
++	for (i = 0; i < ARRAY_SIZE(fixes); ++i) {
++		err = mv88e6390_serdes_read(chip, lane, fixes[i].dev,
++					    fixes[i].reg, &reg);
++		if (err)
++			return err;
++
++		reg &= ~fixes[i].mask;
++		reg |= fixes[i].val;
++
++		err = mv88e6390_serdes_write(chip, lane, fixes[i].dev,
++					     fixes[i].reg, reg);
++		if (err)
++			return err;
++	}
++
++	return 0;
++}
++
++static int mv88e6393x_serdes_fix_2500basex_an(struct mv88e6xxx_chip *chip,
++					      int lane, u8 cmode, bool on)
++{
++	u16 reg;
++	int err;
++
++	if (cmode != MV88E6XXX_PORT_STS_CMODE_2500BASEX)
++		return 0;
++
++	/* Inband AN is broken on Amethyst in 2500base-x mode when set by
++	 * standard mechanism (via cmode).
++	 * We can get around this by configuring the PCS mode to 1000base-x
++	 * and then writing value 0x58 to register 1e.8000. (This must be done
++	 * while SerDes receiver and transmitter are disabled, which is, when
++	 * this function is called.)
++	 * It seem that when we do this configuration to 2500base-x mode (by
++	 * changing PCS mode to 1000base-x and frequency to 3.125 GHz from
++	 * 1.25 GHz) and then configure to sgmii or 1000base-x, the device
++	 * thinks that it already has SerDes at 1.25 GHz and does not change
++	 * the 1e.8000 register, leaving SerDes at 3.125 GHz.
++	 * To avoid this, change PCS mode back to 2500base-x when disabling
++	 * SerDes from 2500base-x mode.
++	 */
++	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++				    MV88E6393X_SERDES_POC, &reg);
++	if (err)
++		return err;
++
++	reg &= ~(MV88E6393X_SERDES_POC_PCS_MASK | MV88E6393X_SERDES_POC_AN);
++	if (on)
++		reg |= MV88E6393X_SERDES_POC_PCS_1000BASEX |
++		       MV88E6393X_SERDES_POC_AN;
++	else
++		reg |= MV88E6393X_SERDES_POC_PCS_2500BASEX;
++	reg |= MV88E6393X_SERDES_POC_RESET;
++
++	err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
++				     MV88E6393X_SERDES_POC, reg);
++	if (err)
++		return err;
++
++	err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_VEND1, 0x8000, 0x58);
++	if (err)
++		return err;
++
++	return 0;
++}
++
++int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
++			    bool on)
++{
++	u8 cmode = chip->ports[port].cmode;
++	int err;
++
++	if (port != 0 && port != 9 && port != 10)
++		return -EOPNOTSUPP;
++
++	if (on) {
++		err = mv88e6393x_serdes_erratum_4_8(chip, lane);
++		if (err)
++			return err;
++
++		err = mv88e6393x_serdes_erratum_5_2(chip, lane, cmode);
++		if (err)
++			return err;
++
++		err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode,
++							 true);
++		if (err)
++			return err;
++
++		err = mv88e6393x_serdes_power_lane(chip, lane, true);
++		if (err)
++			return err;
++	}
++
++	switch (cmode) {
++	case MV88E6XXX_PORT_STS_CMODE_SGMII:
++	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
++	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
++		err = mv88e6390_serdes_power_sgmii(chip, lane, on);
++		break;
++	case MV88E6393X_PORT_STS_CMODE_5GBASER:
++	case MV88E6393X_PORT_STS_CMODE_10GBASER:
++		err = mv88e6390_serdes_power_10g(chip, lane, on);
++		break;
++	default:
++		err = -EINVAL;
++		break;
++	}
++
++	if (err)
++		return err;
++
++	if (!on) {
++		err = mv88e6393x_serdes_power_lane(chip, lane, false);
++		if (err)
++			return err;
++
++		err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode,
++							 false);
++	}
++
++	return err;
++}
+diff -rNu a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h
+--- a/drivers/net/dsa/mv88e6xxx/serdes.h	2022-01-16 10:14:24.000000000 +0200
++++ b/drivers/net/dsa/mv88e6xxx/serdes.h	2022-01-17 15:23:08.965745917 +0200
+@@ -42,6 +42,9 @@
+ /* 10GBASE-R and 10GBASE-X4/X2 */
+ #define MV88E6390_10G_CTRL1		(0x1000 + MDIO_CTRL1)
+ #define MV88E6390_10G_STAT1		(0x1000 + MDIO_STAT1)
++#define MV88E6393X_10G_INT_ENABLE	0x9000
++#define MV88E6393X_10G_INT_LINK_CHANGE	BIT(2)
++#define MV88E6393X_10G_INT_STATUS	0x9001
+ 
+ /* 1000BASE-X and SGMII */
+ #define MV88E6390_SGMII_BMCR		(0x2000 + MII_BMCR)
+@@ -73,46 +76,81 @@
+ #define MV88E6390_PG_CONTROL		0xf010
+ #define MV88E6390_PG_CONTROL_ENABLE_PC		BIT(0)
+ 
+-u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
+-u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
+-u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
+-u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
++#define MV88E6393X_PORT0_LANE			0x00
++#define MV88E6393X_PORT9_LANE			0x09
++#define MV88E6393X_PORT10_LANE			0x0a
++
++/* Port Operational Configuration */
++#define MV88E6393X_SERDES_POC			0xf002
++#define MV88E6393X_SERDES_POC_PCS_1000BASEX	0x0000
++#define MV88E6393X_SERDES_POC_PCS_2500BASEX	0x0001
++#define MV88E6393X_SERDES_POC_PCS_SGMII_PHY	0x0002
++#define MV88E6393X_SERDES_POC_PCS_SGMII_MAC	0x0003
++#define MV88E6393X_SERDES_POC_PCS_5GBASER	0x0004
++#define MV88E6393X_SERDES_POC_PCS_10GBASER	0x0005
++#define MV88E6393X_SERDES_POC_PCS_USXGMII_PHY	0x0006
++#define MV88E6393X_SERDES_POC_PCS_USXGMII_MAC	0x0007
++#define MV88E6393X_SERDES_POC_PCS_MASK		0x0007
++#define MV88E6393X_SERDES_POC_RESET		BIT(15)
++#define MV88E6393X_SERDES_POC_PDOWN		BIT(5)
++#define MV88E6393X_SERDES_POC_AN		BIT(3)
++#define MV88E6393X_SERDES_CTRL1			0xf003
++#define MV88E6393X_SERDES_CTRL1_TX_PDOWN	BIT(9)
++#define MV88E6393X_SERDES_CTRL1_RX_PDOWN	BIT(8)
++
++#define MV88E6393X_ERRATA_4_8_REG		0xF074
++#define MV88E6393X_ERRATA_4_8_BIT		BIT(14)
++
++int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
++int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
++int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
++int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
++int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
+ int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
+-				u8 lane, unsigned int mode,
++				int lane, unsigned int mode,
+ 				phy_interface_t interface,
+ 				const unsigned long *advertise);
+ int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
+-				u8 lane, unsigned int mode,
++				int lane, unsigned int mode,
+ 				phy_interface_t interface,
+ 				const unsigned long *advertise);
+ int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
+-				   u8 lane, struct phylink_link_state *state);
++				   int lane, struct phylink_link_state *state);
+ int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
+-				   u8 lane, struct phylink_link_state *state);
++				   int lane, struct phylink_link_state *state);
++int mv88e6393x_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
++				    int lane, struct phylink_link_state *state);
+ int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
+-				    u8 lane);
++				    int lane);
+ int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
+-				    u8 lane);
++				    int lane);
+ int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
+-				 u8 lane, int speed, int duplex);
++				 int lane, int speed, int duplex);
+ int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
+-				 u8 lane, int speed, int duplex);
++				 int lane, int speed, int duplex);
+ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
+ 					  int port);
+ unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
+ 					  int port);
+-int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
++int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
+ 			   bool on);
+-int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
++int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
+ 			   bool on);
+-int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
++int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
++			    bool on);
++int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip);
++int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
+ 				bool enable);
+-int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
++int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
+ 				bool enable);
++int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
++				 int lane, bool enable);
+ irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
+-					u8 lane);
++					int lane);
+ irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
+-					u8 lane);
++					int lane);
++irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
++					 int lane);
+ int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
+ int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
+ 				 int port, uint8_t *data);
+@@ -129,18 +167,18 @@
+ int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port);
+ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p);
+ 
+-/* Return the (first) SERDES lane address a port is using, 0 otherwise. */
+-static inline u8 mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip,
+-					   int port)
++/* Return the (first) SERDES lane address a port is using, -errno otherwise. */
++static inline int mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip,
++					    int port)
+ {
+ 	if (!chip->info->ops->serdes_get_lane)
+-		return 0;
++		return -EOPNOTSUPP;
+ 
+ 	return chip->info->ops->serdes_get_lane(chip, port);
+ }
+ 
+ static inline int mv88e6xxx_serdes_power_up(struct mv88e6xxx_chip *chip,
+-					    int port, u8 lane)
++					    int port, int lane)
+ {
+ 	if (!chip->info->ops->serdes_power)
+ 		return -EOPNOTSUPP;
+@@ -149,7 +187,7 @@
+ }
+ 
+ static inline int mv88e6xxx_serdes_power_down(struct mv88e6xxx_chip *chip,
+-					      int port, u8 lane)
++					      int port, int lane)
+ {
+ 	if (!chip->info->ops->serdes_power)
+ 		return -EOPNOTSUPP;
+@@ -167,7 +205,7 @@
+ }
+ 
+ static inline int mv88e6xxx_serdes_irq_enable(struct mv88e6xxx_chip *chip,
+-					      int port, u8 lane)
++					      int port, int lane)
+ {
+ 	if (!chip->info->ops->serdes_irq_enable)
+ 		return -EOPNOTSUPP;
+@@ -176,7 +214,7 @@
+ }
+ 
+ static inline int mv88e6xxx_serdes_irq_disable(struct mv88e6xxx_chip *chip,
+-					       int port, u8 lane)
++					       int port, int lane)
+ {
+ 	if (!chip->info->ops->serdes_irq_enable)
+ 		return -EOPNOTSUPP;
+@@ -185,7 +223,7 @@
+ }
+ 
+ static inline irqreturn_t
+-mv88e6xxx_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, u8 lane)
++mv88e6xxx_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, int lane)
+ {
+ 	if (!chip->info->ops->serdes_irq_status)
+ 		return IRQ_NONE;
-- 
2.30.2


From 5bbeb03090f3b91edc8ae986d2e0e6038a0b89ed Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Wed, 9 Feb 2022 09:07:57 +0200
Subject: [PATCH 02/11] generic: fix mikrotik/rb_softconfig work(crc mismatch)
 with 64K block size

Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 .../files/drivers/platform/mikrotik/rb_softconfig.c        | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c
index 5acff6aa91..2332089a46 100644
--- a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c
+++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c
@@ -58,6 +58,7 @@
 
 #define RB_SOFTCONFIG_VER		"0.05"
 #define RB_SC_PR_PFX			"[rb_softconfig] "
+#define SOFT_CONF_FIXED_SIZE 0x1000
 
 #define RB_SC_HAS_WRITE_SUPPORT	true
 #define RB_SC_WMODE			S_IWUSR
@@ -636,7 +637,7 @@ static ssize_t sc_commit_store(struct kobject *kobj, struct kobj_attribute *attr
 
 	write_lock(&sc_bufrwl);
 	if (!flush)	// reread
-		ret = mtd_read(mtd, 0, mtd->size, &bytes_rw, sc_buf);
+		ret = mtd_read(mtd, 0, sc_buflen, &bytes_rw, sc_buf);
 	else {	// crc32 + commit
 		/*
 		 * CRC32 is computed on the entire buffer, excluding the CRC
@@ -658,7 +659,7 @@ static ssize_t sc_commit_store(struct kobject *kobj, struct kobj_attribute *attr
 		ei.len = mtd->size;
 		ret = mtd_erase(mtd, &ei);
 		if (!ret)
-			ret = mtd_write(mtd, 0, mtd->size, &bytes_rw, sc_buf);
+			ret = mtd_write(mtd, 0, sc_buflen, &bytes_rw, sc_buf);
 
 		/*
 		 * Handling mtd_write() failure here is a tricky situation. The
@@ -708,7 +709,7 @@ int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd)
 	if (ret)
 		return -ENODEV;
 
-	sc_buflen = mtd->size;
+	sc_buflen = mtd->size > SOFT_CONF_FIXED_SIZE ? SOFT_CONF_FIXED_SIZE : mtd->size;
 	sc_buf = kmalloc(sc_buflen, GFP_KERNEL);
 	if (!sc_buf) {
 		__put_mtd_device(mtd);
-- 
2.30.2


From f450c4aa3a0ae94da17d7caba9864ab1e448dda9 Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Wed, 9 Feb 2022 09:32:30 +0200
Subject: [PATCH 03/11] mvebu: add aux-loader2 support

This auxiliary bootloader implements the ability to run the OpenWrt Linux kernel(FIT image)
from the RouterBOOT. The thing is that RouterBOOT is able to load ONLY program code in ELF format.
To bypass this restriction, the auxiliary bootloader extends the capabilities of the RouterBOOT -
by adding support for loading system from FIT images.

Source code is available under GPLv2: https://github.com/adron-s/aux-loader2

Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 target/linux/mvebu/image/Makefile           |  12 ++++++++++++
 target/linux/mvebu/image/bin/aux-loader.elf | Bin 0 -> 65880 bytes
 target/linux/mvebu/image/cortexa72.mk       |  19 +++++++++++++++++++
 3 files changed, 31 insertions(+)
 create mode 100644 target/linux/mvebu/image/bin/aux-loader.elf

diff --git a/target/linux/mvebu/image/Makefile b/target/linux/mvebu/image/Makefile
index 44e66a5f87..20e217580f 100644
--- a/target/linux/mvebu/image/Makefile
+++ b/target/linux/mvebu/image/Makefile
@@ -150,6 +150,18 @@ define Build/uDPU-firmware
 		-f $(KDIR_TMP)/$(DEVICE_IMG_PREFIX)-firmware.tgz -C $@-fw .
 endef
 
+define Device/FitImageLzma
+  KERNEL_SUFFIX := -fit-uImage.itb
+  KERNEL := kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(DEVICE_DTS).dtb | pad-to 64k
+  KERNEL_NAME := Image
+endef
+
+define Build/aux-loader
+  -[ -f "$@" ] && [ -f "bin/aux-loader.elf" ] &&	\
+ 	cat "bin/aux-loader.elf" "$@" > "$@.tmp" &&	\
+ 	cat "$@.tmp" > "$@" && rm -f "$@.tmp"
+endef
+
 define Device/Default
   PROFILES := Default
   DEVICE_DTS = $$(SOC)-$(lastword $(subst _, ,$(1)))
diff --git a/target/linux/mvebu/image/bin/aux-loader.elf b/target/linux/mvebu/image/bin/aux-loader.elf
new file mode 100644
index 0000000000000000000000000000000000000000..a8f19ec360ff92cb2e77a052fcc2502e612bb413
GIT binary patch
literal 65880
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
BUa$ZF

literal 0
HcmV?d00001

diff --git a/target/linux/mvebu/image/cortexa72.mk b/target/linux/mvebu/image/cortexa72.mk
index f95ce533ea..52384c3098 100644
--- a/target/linux/mvebu/image/cortexa72.mk
+++ b/target/linux/mvebu/image/cortexa72.mk
@@ -52,6 +52,25 @@ define Device/marvell_macchiatobin-singleshot
 endef
 TARGET_DEVICES += marvell_macchiatobin-singleshot
 
+define Device/mikrotik_rb5009
+  DEVICE_DTS := armada-7040-rb5009
+  DEVICE_DTS_DIR := $(DTS_DIR)/marvell
+  $(call Device/FitImageLzma)
+  DEVICE_VENDOR := MikroTik
+  DEVICE_MODEL := RB5009
+  SOC := armada-7040
+  KERNEL_LOADADDR := 0x22000000
+  KERNEL_INITRAMFS = $$(KERNEL) | aux-loader
+  KERNEL_INITRAMFS_SUFFIX := -fit-uImage.elf
+  BLOCKSIZE := 64k
+  IMAGE_SIZE := 15360k
+  IMAGES := sysupgrade.bin
+  IMAGE/sysupgrade.bin := append-kernel | pad-to $$(BLOCKSIZE) | \
+  	append-rootfs | pad-rootfs | check-size | append-metadata
+  DEVICE_PACKAGES += kmod-i2c-gpio
+endef
+TARGET_DEVICES += mikrotik_rb5009
+
 define Device/marvell_clearfog-gt-8k
   $(call Device/Default-arm64)
   DEVICE_VENDOR := SolidRun
-- 
2.30.2


From 7fb4e6ce7b4aff4743dedc0fc8436d2421a716c5 Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Wed, 9 Feb 2022 09:36:01 +0200
Subject: [PATCH 04/11] mvebu: cortexa72: enable SBSA Watchdog, Mikrotik RB
 Sysfs and MTD_SPLIT_FIT_FW

Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 target/linux/mvebu/cortexa72/config-5.10 | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/target/linux/mvebu/cortexa72/config-5.10 b/target/linux/mvebu/cortexa72/config-5.10
index 8d1ec7a283..74cf0a6ce2 100644
--- a/target/linux/mvebu/cortexa72/config-5.10
+++ b/target/linux/mvebu/cortexa72/config-5.10
@@ -30,6 +30,7 @@ CONFIG_ARM_GIC_V3_ITS=y
 CONFIG_ARM_GIC_V3_ITS_PCI=y
 # CONFIG_ARM_PL172_MPMC is not set
 CONFIG_ARM_PSCI_FW=y
+CONFIG_ARM_SBSA_WATCHDOG=y
 CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
 CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y
 CONFIG_CRYPTO_AES_ARM64=y
@@ -97,3 +98,6 @@ CONFIG_XXHASH=y
 CONFIG_ZONE_DMA32=y
 CONFIG_ZSTD_COMPRESS=y
 CONFIG_ZSTD_DECOMPRESS=y
+CONFIG_MIKROTIK=y
+CONFIG_MIKROTIK_RB_SYSFS=y
+CONFIG_MTD_SPLIT_FIT_FW=y
-- 
2.30.2


From 8d92224c28f080bf6dd0ebc2769ae9f952a7e987 Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Wed, 9 Feb 2022 09:45:47 +0200
Subject: [PATCH 05/11] mvebu: mvebu: add support for RB5009UG+S+IN.

This patch adds support for Mikrotik RB5009UG+S+IN.

Specifications:
  - SoC: Marvell Armada 7040 (88F7040) - 4 cores, ARMv8, 1.4GHz, 64bit
  - RAM: 1024MB DDR4
  - Flash: 16MB SPI NOR flash, 1024MB NAND
  - Ethernet: One Marvell 88E6393X - Amethyst: one 2.5G + seven 1G ports and one SFP+
  - LED: User, SFP, Hdr1, Hdr2
  - Buttons: Reset
  - UART: 115200 8n1
  - USB: One USB3 port
Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 .../base-files/etc/board.d/02_network         |   3 +
 .../base-files/lib/upgrade/platform.sh        |   4 +
 .../boot/dts/marvell/armada-7040-rb5009.dts   | 377 ++++++++++++++++++
 3 files changed, 384 insertions(+)
 create mode 100644 target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts

diff --git a/target/linux/mvebu/cortexa72/base-files/etc/board.d/02_network b/target/linux/mvebu/cortexa72/base-files/etc/board.d/02_network
index 6a5861084e..ccb89add24 100644
--- a/target/linux/mvebu/cortexa72/base-files/etc/board.d/02_network
+++ b/target/linux/mvebu/cortexa72/base-files/etc/board.d/02_network
@@ -32,6 +32,9 @@ marvell,armada7040-db)
 marvell,armada8040-clearfog-gt-8k)
 	ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4 eth2" "eth0 eth1"
 	;;
+mikrotik,rb5009)
+	ucidef_set_interfaces_lan_wan "p2 p3 p4 p5 p6 p7 p8" "sfp p1"
+	;;
 *)
 	ucidef_set_interface_lan "eth0"
 	;;
diff --git a/target/linux/mvebu/cortexa72/base-files/lib/upgrade/platform.sh b/target/linux/mvebu/cortexa72/base-files/lib/upgrade/platform.sh
index 18fdb01959..9c6c2ec2ea 100755
--- a/target/linux/mvebu/cortexa72/base-files/lib/upgrade/platform.sh
+++ b/target/linux/mvebu/cortexa72/base-files/lib/upgrade/platform.sh
@@ -33,6 +33,10 @@ platform_do_upgrade() {
 	marvell,armada8040-clearfog-gt-8k)
 		legacy_sdcard_do_upgrade "$1"
 		;;
+	mikrotik,rb5009)
+		PART_NAME=firmware
+		default_do_upgrade "$1"
+		;;
 	*)
 		default_do_upgrade "$1"
 		;;
diff --git a/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts b/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts
new file mode 100644
index 0000000000..66c74451f0
--- /dev/null
+++ b/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+
+/dts-v1/;
+
+#include "armada-7040.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+	model = "MikroTik RB5009";
+	compatible = "mikrotik,rb5009", "marvell,armada7040",
+		     "marvell,armada-ap806-quad", "marvell,armada-ap806";
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	memory@0 {
+		device_type = "memory";
+		reg = <0x0 0x0 0x0 0x40000000>;
+	};
+
+	aliases {
+		led-boot = &led_user;
+		led-failsafe = &led_user;
+		led-running = &led_user;
+		led-upgrade = &led_user;
+	};
+
+	sfp_i2c: sfp-i2c {
+		compatible = "i2c-gpio";
+		sda-gpios = <&cp0_gpio1 0 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+		scl-gpios = <&cp0_gpio1 1 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+	};
+
+	keys {
+		compatible = "gpio-keys";
+
+		reset {
+			label = "reset";
+			gpios = <&cp0_gpio1 28 GPIO_ACTIVE_LOW>;
+			linux,code = <KEY_RESTART>;
+		};
+	};
+
+	leds {
+		compatible = "gpio-leds";
+
+		led_user: user {
+			label = "green:user";
+			gpios = <&cp0_gpio2 26 GPIO_ACTIVE_LOW>;
+		};
+
+		sfp {
+			label = "green:sfp";
+			gpios = <&cp0_gpio2 25 GPIO_ACTIVE_LOW>;
+		};
+
+		hdr1 {
+			label = "blue:hdr1";
+			gpios = <&cp0_gpio1 4 GPIO_ACTIVE_LOW>;
+		};
+
+		hdr2 {
+			label = "blue:hdr2";
+			gpios = <&cp0_gpio2 19 GPIO_ACTIVE_LOW>;
+		};
+	};
+
+	sfp: sfp {
+		compatible = "sff,sfp";
+		i2c-bus = <&sfp_i2c>;
+	};
+};
+
+&uart0 {
+	status = "okay";
+
+	pinctrl-0 = <&uart0_pins>;
+	pinctrl-names = "default";
+};
+
+
+&spi0 {
+	status = "okay";
+
+	spi-flash@0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "jedec,spi-nor";
+		reg = <0>;
+		spi-max-frequency = <20000000>;
+
+		partitions {
+			compatible = "fixed-partitions";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			partition@0 {
+				label = "ATF";
+				reg = <0x0 0x95c04>;
+				read-only;
+			};
+			partition@1 {
+				label = "backup_RouterBOOT";
+				reg = <0x95c04 0x193FC>;
+				read-only;
+			};
+			hard_config: partition@2 {
+				label = "hard_config";
+				reg = <0xaf000 0x1000>;
+				read-only;
+			};
+			partition@3 {
+				label = "RouterBOOT";
+				reg = <0xb0000 0x10000>;
+			};
+			partition@4 {
+				label = "soft_config";
+				reg = <0xc0000 0x10000>;
+			};
+			partition@5 {
+				label = "DTS";
+				reg = <0xd0000 0x10000>;
+				read-only;
+			};
+			partition@6 {
+				label = "free_space";
+				reg = <0xe0000 0x20000>;
+			};
+			partition@7 {
+				compatible = "denx,fit";
+				label = "firmware";
+				reg = <0x100000 0xf00000>;
+			};
+			partition@8 {
+				label = "first_1M";
+				reg = <0x0 0x100000>;
+			};
+		};
+	};
+};
+
+&cp0_nand_controller {
+	status = "okay";
+
+	nand@0 {
+		reg = <0>;
+		nand-rb = <0>;
+		nand-ecc-mode = "hw";
+		nand-ecc-strength = <4>;
+		nand-ecc-step-size = <512>;
+		nand-on-flash-bbt;
+
+		partitions {
+			compatible = "fixed-partitions";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			partition@0 {
+				label = "RouterBoard NAND Boot";
+				reg = <0x0 0x800000>;
+			};
+
+			partition@800000 {
+				label = "RouterBoard NAND Main";
+				reg = <0x800000 0x3f800000>;
+			};
+		};
+	};
+};
+
+&cp0_gpio2 {
+	enable-usb-power {
+		gpio-hog;
+		gpios = <23 GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "enable USB power";
+	};
+
+	enable-leds-power {
+		gpio-hog;
+		gpios = <27 GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "enable LED-s power";
+	};
+};
+
+&cp0_usb3_1 {
+	status = "okay";
+};
+
+&cp0_i2c0 {
+	status = "okay";
+	clock-frequency = <100000>;
+};
+
+&cp0_mdio {
+	status = "okay";
+};
+
+&cp0_ethernet {
+	status = "okay";
+};
+
+&cp0_eth0 {
+	/* This port is connected to 88E6393X switch */
+	status = "okay";
+	phy-mode = "10gbase-r";
+	managed = "in-band-status";
+	nvmem-cells = <&macaddr_hard>;
+	nvmem-cell-names = "mac-address";
+	mac-address-increment = <0>;
+};
+
+&cp0_mdio {
+	status = "okay";
+
+	switch@0 {
+		/* Actual device is MV88E6393X */
+		compatible = "marvell,mv88e6190";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0>;
+		//strict-cpu-mode = <1>;
+		//reset-gpios = <&cp0_gpio2 2 GPIO_ACTIVE_HIGH>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				label = "cpu";
+				ethernet = <&cp0_eth0>;
+			};
+
+			port@1 {
+				reg = <1>;
+				label = "p8";
+				phy-handle = <&switch0phy1>;
+				nvmem-cells = <&macaddr_hard>;
+				nvmem-cell-names = "mac-address";
+				mac-address-increment = <7>;
+			};
+
+			port@2 {
+				reg = <2>;
+				label = "p7";
+				phy-handle = <&switch0phy2>;
+				nvmem-cells = <&macaddr_hard>;
+				nvmem-cell-names = "mac-address";
+				mac-address-increment = <6>;
+			};
+
+			port@3 {
+				reg = <3>;
+				label = "p6";
+				phy-handle = <&switch0phy3>;
+				nvmem-cells = <&macaddr_hard>;
+				nvmem-cell-names = "mac-address";
+				mac-address-increment = <5>;
+			};
+
+			port@4 {
+				reg = <4>;
+				label = "p5";
+				phy-handle = <&switch0phy4>;
+				nvmem-cells = <&macaddr_hard>;
+				nvmem-cell-names = "mac-address";
+				mac-address-increment = <4>;
+			};
+
+			port@5 {
+				reg = <5>;
+				label = "p4";
+				phy-handle = <&switch0phy5>;
+				nvmem-cells = <&macaddr_hard>;
+				nvmem-cell-names = "mac-address";
+				mac-address-increment = <3>;
+			};
+
+			port@6 {
+				reg = <6>;
+				label = "p3";
+				phy-handle = <&switch0phy6>;
+				nvmem-cells = <&macaddr_hard>;
+				nvmem-cell-names = "mac-address";
+				mac-address-increment = <2>;
+			};
+
+			port@7 {
+				reg = <7>;
+				label = "p2";
+				phy-handle = <&switch0phy7>;
+				nvmem-cells = <&macaddr_hard>;
+				nvmem-cell-names = "mac-address";
+				mac-address-increment = <1>;
+			};
+
+			port@9 {
+				reg = <9>;
+				label = "p1";
+				phy-mode = "sgmii";
+				phy-handle = <&switch0phy9>;
+				managed = "in-band-status";
+				nvmem-cells = <&macaddr_hard>;
+				nvmem-cell-names = "mac-address";
+				mac-address-increment = <0>;
+			};
+
+			port@a {
+				reg = <10>;
+				label = "sfp";
+				phy-mode = "10gbase-r";
+				managed = "in-band-status";
+				sfp = <&sfp>;
+				nvmem-cells = <&macaddr_hard>;
+				nvmem-cell-names = "mac-address";
+				mac-address-increment = <8>;
+			};
+		};
+
+		mdio {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			switch0phy1: switch0phy1@1 {
+				reg = <0x1>;
+			};
+
+			switch0phy2: switch0phy2@2 {
+				reg = <0x2>;
+			};
+
+			switch0phy3: switch0phy3@3 {
+				reg = <0x3>;
+			};
+
+			switch0phy4: switch0phy4@4 {
+				reg = <0x4>;
+			};
+
+			switch0phy5: switch0phy5@5 {
+				reg = <0x5>;
+			};
+
+			switch0phy6: switch0phy6@6 {
+				reg = <0x6>;
+			};
+
+			switch0phy7: switch0phy7@7 {
+				reg = <0x7>;
+			};
+		};
+
+		mdio1 {
+			compatible = "marvell,mv88e6xxx-mdio-external";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			switch0phy9: switch0phy9@0 {
+				reg = <0>;
+			};
+		};
+	};
+};
+
+&hard_config {
+	compatible = "nvmem-cells";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	macaddr_hard: macaddr@10 {
+		reg = <0x10 0x6>;
+	};
+};
-- 
2.30.2


From c1b2253adba5b31b2ad9616ef7b2571fa57bdca8 Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Wed, 9 Feb 2022 10:23:06 +0200
Subject: [PATCH 06/11] update README.md

Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 README.md | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/README.md b/README.md
index 2eff292321..813f3f04b5 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,30 @@
 ![OpenWrt logo](include/logo.png)
 
+## The installation process for Mikrotik RB5009:
+
+tftpboot from [ openwrt-mvebu-cortexa72-mikrotik_rb5009-initramfs-fit-uImage.elf ](https://github.com/adron-s/openwrt-rb5009/releases/download/rb5009-20220209/openwrt-mvebu-cortexa72-mikrotik_rb5009-initramfs-fit-uImage.elf)
+
+To do this, prepare the tftp server and run the following commands on the RouterOS:
+
+```
+/system/routerboard/settings/set boot-device=ethernet
+/system/reboot
+```
+
+Log in(via SSH) to the newly bootted initramfs OpenWrt and run the following
+commands on it:
+
+```
+wget https://github.com/adron-s/aux-loader2/raw/main/releases/2.00-latest/rbt-with-aux-for-mtd5.bin \
+  -O- | mtd write - RouterBOOT
+echo cfg > /sys/firmware/mikrotik/soft_config/boot_device
+echo 1 > /sys/firmware/mikrotik/soft_config/commit
+
+wget https://github.com/adron-s/openwrt-rb5009/releases/download/rb5009-20220209/openwrt-mvebu-cortexa72-mikrotik_rb5009-squashfs-sysupgrade.bin \
+  -O- > /tmp/fw.bin && sysupgrade /tmp/fw.bin
+```
+## That's all. Your RB5009 is ready to be used with OpenWrt.
+
 OpenWrt Project is a Linux operating system targeting embedded devices. Instead
 of trying to create a single, static firmware, OpenWrt provides a fully
 writable filesystem with package management. This frees you from the
-- 
2.30.2


From 21d5420118da40692e6e35e503329a1b9ea98946 Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Mon, 14 Feb 2022 06:50:56 +0200
Subject: [PATCH 07/11] mvebu: RB5009: start to use UBI

Now UBI on a NAND flash drive is used as rootfs.
The kernel is still located on a NOR flash drive in the form of a FIT image.

Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 .../cortexa72/base-files/lib/upgrade/platform.sh     |  3 +--
 .../arm64/boot/dts/marvell/armada-7040-rb5009.dts    | 12 +++---------
 target/linux/mvebu/image/cortexa72.mk                |  6 +-----
 3 files changed, 5 insertions(+), 16 deletions(-)

diff --git a/target/linux/mvebu/cortexa72/base-files/lib/upgrade/platform.sh b/target/linux/mvebu/cortexa72/base-files/lib/upgrade/platform.sh
index 9c6c2ec2ea..57cd3089bd 100755
--- a/target/linux/mvebu/cortexa72/base-files/lib/upgrade/platform.sh
+++ b/target/linux/mvebu/cortexa72/base-files/lib/upgrade/platform.sh
@@ -34,8 +34,7 @@ platform_do_upgrade() {
 		legacy_sdcard_do_upgrade "$1"
 		;;
 	mikrotik,rb5009)
-		PART_NAME=firmware
-		default_do_upgrade "$1"
+		nand_do_upgrade "$1"
 		;;
 	*)
 		default_do_upgrade "$1"
diff --git a/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts b/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts
index 66c74451f0..5535f8ad89 100644
--- a/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts
+++ b/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts
@@ -129,8 +129,7 @@
 				reg = <0xe0000 0x20000>;
 			};
 			partition@7 {
-				compatible = "denx,fit";
-				label = "firmware";
+				label = "kernel";
 				reg = <0x100000 0xf00000>;
 			};
 			partition@8 {
@@ -158,13 +157,8 @@
 			#size-cells = <1>;
 
 			partition@0 {
-				label = "RouterBoard NAND Boot";
-				reg = <0x0 0x800000>;
-			};
-
-			partition@800000 {
-				label = "RouterBoard NAND Main";
-				reg = <0x800000 0x3f800000>;
+				label = "ubi";
+				reg = <0x0 0x40000000>;
 			};
 		};
 	};
diff --git a/target/linux/mvebu/image/cortexa72.mk b/target/linux/mvebu/image/cortexa72.mk
index 52384c3098..f7c92a0f72 100644
--- a/target/linux/mvebu/image/cortexa72.mk
+++ b/target/linux/mvebu/image/cortexa72.mk
@@ -56,17 +56,13 @@ define Device/mikrotik_rb5009
   DEVICE_DTS := armada-7040-rb5009
   DEVICE_DTS_DIR := $(DTS_DIR)/marvell
   $(call Device/FitImageLzma)
+  $(Device/NAND-128K)
   DEVICE_VENDOR := MikroTik
   DEVICE_MODEL := RB5009
   SOC := armada-7040
   KERNEL_LOADADDR := 0x22000000
   KERNEL_INITRAMFS = $$(KERNEL) | aux-loader
   KERNEL_INITRAMFS_SUFFIX := -fit-uImage.elf
-  BLOCKSIZE := 64k
-  IMAGE_SIZE := 15360k
-  IMAGES := sysupgrade.bin
-  IMAGE/sysupgrade.bin := append-kernel | pad-to $$(BLOCKSIZE) | \
-  	append-rootfs | pad-rootfs | check-size | append-metadata
   DEVICE_PACKAGES += kmod-i2c-gpio
 endef
 TARGET_DEVICES += mikrotik_rb5009
-- 
2.30.2


From 533beabeb0d6baebdf0f8856e4a95b81079c65e2 Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Mon, 14 Feb 2022 17:23:20 +0200
Subject: [PATCH 08/11] mvebu: RB5009: update aux-loader.elf

Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 README.md                                   |   2 +-
 target/linux/mvebu/image/bin/aux-loader.elf | Bin 65880 -> 67224 bytes
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 813f3f04b5..9fa1c01854 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Log in(via SSH) to the newly bootted initramfs OpenWrt and run the following
 commands on it:
 
 ```
-wget https://github.com/adron-s/aux-loader2/raw/main/releases/2.00-latest/rbt-with-aux-for-mtd5.bin \
+wget https://github.com/adron-s/aux-loader2/raw/main/releases/2.xx-latest/rbt-with-aux-for-mtd5.bin \
   -O- | mtd write - RouterBOOT
 echo cfg > /sys/firmware/mikrotik/soft_config/boot_device
 echo 1 > /sys/firmware/mikrotik/soft_config/commit
diff --git a/target/linux/mvebu/image/bin/aux-loader.elf b/target/linux/mvebu/image/bin/aux-loader.elf
index a8f19ec360ff92cb2e77a052fcc2502e612bb413..0fa036ef9907bb1ee2efcdca8de2f9c3af4a0c6c 100644
GIT binary patch
delta 5669
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
F`VYz>t>gdz

delta 4395
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

-- 
2.30.2


From bc63c86213a3e8c68758d72780addf5dd22aed52 Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Tue, 15 Feb 2022 07:33:20 +0200
Subject: [PATCH 09/11] mvebu: patches-5.10: backport QCA8081 PHY support

Backport support for the Qualcomm QCA8081 PHY:
  https://github.com/adron-s/qca8081-backport-5.1

Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 target/linux/mvebu/config-5.10                |   1 +
 .../711-net-qca8081-phy-support.patch         | 665 ++++++++++++++++++
 2 files changed, 666 insertions(+)
 create mode 100644 target/linux/mvebu/patches-5.10/711-net-qca8081-phy-support.patch

diff --git a/target/linux/mvebu/config-5.10 b/target/linux/mvebu/config-5.10
index 0bce22daee..dc1c318745 100644
--- a/target/linux/mvebu/config-5.10
+++ b/target/linux/mvebu/config-5.10
@@ -424,3 +424,4 @@ CONFIG_ZBOOT_ROM_BSS=0x0
 CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZLIB_DEFLATE=y
 CONFIG_ZLIB_INFLATE=y
+CONFIG_AT803X_PHY=y
diff --git a/target/linux/mvebu/patches-5.10/711-net-qca8081-phy-support.patch b/target/linux/mvebu/patches-5.10/711-net-qca8081-phy-support.patch
new file mode 100644
index 0000000000..48f24a50c9
--- /dev/null
+++ b/target/linux/mvebu/patches-5.10/711-net-qca8081-phy-support.patch
@@ -0,0 +1,665 @@
+diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
+index f3c701f..aceb178 100644
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -41,6 +41,9 @@
+ #define AT803X_SS_SPEED_DUPLEX_RESOLVED		BIT(11)
+ #define AT803X_SS_MDIX				BIT(6)
+ 
++#define QCA808X_SS_SPEED_MASK			GENMASK(9, 7)
++#define QCA808X_SS_SPEED_2500			4
++
+ #define AT803X_INTR_ENABLE			0x12
+ #define AT803X_INTR_ENABLE_AUTONEG_ERR		BIT(15)
+ #define AT803X_INTR_ENABLE_SPEED_CHANGED	BIT(14)
+@@ -155,6 +158,8 @@
+ #define AT803X_PAGE_FIBER		0
+ #define AT803X_PAGE_COPPER		1
+ 
++#define QCA8081_PHY_ID				0x004dd101
++
+ #define QCA8327_A_PHY_ID			0x004dd033
+ #define QCA8327_B_PHY_ID			0x004dd034
+ #define QCA8337_PHY_ID				0x004dd036
+@@ -162,7 +167,84 @@
+ 
+ #define QCA8K_DEVFLAGS_REVISION_MASK		GENMASK(2, 0)
+ 
+-MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
++/* ADC threshold */
++#define QCA808X_PHY_DEBUG_ADC_THRESHOLD		0x2c80
++#define QCA808X_ADC_THRESHOLD_MASK		GENMASK(7, 0)
++#define QCA808X_ADC_THRESHOLD_80MV		0
++#define QCA808X_ADC_THRESHOLD_100MV		0xf0
++#define QCA808X_ADC_THRESHOLD_200MV		0x0f
++#define QCA808X_ADC_THRESHOLD_300MV		0xff
++
++/* CLD control */
++#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7		0x8007
++#define QCA808X_8023AZ_AFE_CTRL_MASK		GENMASK(8, 4)
++#define QCA808X_8023AZ_AFE_EN			0x90
++
++/* AZ control */
++#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL	0x8008
++#define QCA808X_MMD3_AZ_TRAINING_VAL		0x1c32
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB	0x8014
++#define QCA808X_MSE_THRESHOLD_20DB_VALUE	0x529
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB	0x800E
++#define QCA808X_MSE_THRESHOLD_17DB_VALUE	0x341
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB	0x801E
++#define QCA808X_MSE_THRESHOLD_27DB_VALUE	0x419
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB	0x8020
++#define QCA808X_MSE_THRESHOLD_28DB_VALUE	0x341
++
++#define QCA808X_PHY_MMD7_TOP_OPTION1		0x901c
++#define QCA808X_TOP_OPTION1_DATA		0x0
++
++#define QCA808X_PHY_MMD3_DEBUG_1		0xa100
++#define QCA808X_MMD3_DEBUG_1_VALUE		0x9203
++#define QCA808X_PHY_MMD3_DEBUG_2		0xa101
++#define QCA808X_MMD3_DEBUG_2_VALUE		0x48ad
++#define QCA808X_PHY_MMD3_DEBUG_3		0xa103
++#define QCA808X_MMD3_DEBUG_3_VALUE		0x1698
++#define QCA808X_PHY_MMD3_DEBUG_4		0xa105
++#define QCA808X_MMD3_DEBUG_4_VALUE		0x8001
++#define QCA808X_PHY_MMD3_DEBUG_5		0xa106
++#define QCA808X_MMD3_DEBUG_5_VALUE		0x1111
++#define QCA808X_PHY_MMD3_DEBUG_6		0xa011
++#define QCA808X_MMD3_DEBUG_6_VALUE		0x5f85
++
++/* master/slave seed config */
++#define QCA808X_PHY_DEBUG_LOCAL_SEED		9
++#define QCA808X_MASTER_SLAVE_SEED_ENABLE	BIT(1)
++#define QCA808X_MASTER_SLAVE_SEED_CFG		GENMASK(12, 2)
++#define QCA808X_MASTER_SLAVE_SEED_RANGE		0x32
++
++/* Hibernation yields lower power consumpiton in contrast with normal operation mode.
++ * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.
++ */
++#define QCA808X_DBG_AN_TEST			0xb
++#define QCA808X_HIBERNATION_EN			BIT(15)
++
++#define QCA808X_CDT_ENABLE_TEST			BIT(15)
++#define QCA808X_CDT_INTER_CHECK_DIS		BIT(13)
++#define QCA808X_CDT_LENGTH_UNIT			BIT(10)
++
++#define QCA808X_MMD3_CDT_STATUS			0x8064
++#define QCA808X_MMD3_CDT_DIAG_PAIR_A		0x8065
++#define QCA808X_MMD3_CDT_DIAG_PAIR_B		0x8066
++#define QCA808X_MMD3_CDT_DIAG_PAIR_C		0x8067
++#define QCA808X_MMD3_CDT_DIAG_PAIR_D		0x8068
++#define QCA808X_CDT_DIAG_LENGTH			GENMASK(7, 0)
++
++#define QCA808X_CDT_CODE_PAIR_A			GENMASK(15, 12)
++#define QCA808X_CDT_CODE_PAIR_B			GENMASK(11, 8)
++#define QCA808X_CDT_CODE_PAIR_C			GENMASK(7, 4)
++#define QCA808X_CDT_CODE_PAIR_D			GENMASK(3, 0)
++#define QCA808X_CDT_STATUS_STAT_FAIL		0
++#define QCA808X_CDT_STATUS_STAT_NORMAL		1
++#define QCA808X_CDT_STATUS_STAT_OPEN		2
++#define QCA808X_CDT_STATUS_STAT_SHORT		3
++
++MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+ 
+@@ -837,27 +919,9 @@ static int at803x_aneg_done(struct phy_device *phydev)
+ 	return aneg_done;
+ }
+ 
+-static int at803x_read_status(struct phy_device *phydev)
++static int at803x_read_specific_status(struct phy_device *phydev)
+ {
+-	int ss, err, old_link = phydev->link;
+-
+-	/* Update the link, but return if there was an error */
+-	err = genphy_update_link(phydev);
+-	if (err)
+-		return err;
+-
+-	/* why bother the PHY if nothing can have changed */
+-	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+-		return 0;
+-
+-	phydev->speed = SPEED_UNKNOWN;
+-	phydev->duplex = DUPLEX_UNKNOWN;
+-	phydev->pause = 0;
+-	phydev->asym_pause = 0;
+-
+-	err = genphy_read_lpa(phydev);
+-	if (err < 0)
+-		return err;
++	int ss;
+ 
+ 	/* Read the AT8035 PHY-Specific Status register, which indicates the
+ 	 * speed and duplex that the PHY is actually using, irrespective of
+@@ -868,13 +932,19 @@ static int at803x_read_status(struct phy_device *phydev)
+ 		return ss;
+ 
+ 	if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
+-		int sfc;
++		int sfc, speed;
+ 
+ 		sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
+ 		if (sfc < 0)
+ 			return sfc;
+ 
+-		switch (ss & AT803X_SS_SPEED_MASK) {
++		/* qca8081 takes the different bits for speed value from at803x */
++		if (phydev->drv->phy_id == QCA8081_PHY_ID)
++			speed = ss & QCA808X_SS_SPEED_MASK;
++		else
++			speed = ss & AT803X_SS_SPEED_MASK;
++
++		switch (speed) {
+ 		case AT803X_SS_SPEED_10:
+ 			phydev->speed = SPEED_10;
+ 			break;
+@@ -884,6 +954,9 @@ static int at803x_read_status(struct phy_device *phydev)
+ 		case AT803X_SS_SPEED_1000:
+ 			phydev->speed = SPEED_1000;
+ 			break;
++		case QCA808X_SS_SPEED_2500:
++			phydev->speed = SPEED_2500;
++			break;
+ 		}
+ 		if (ss & AT803X_SS_DUPLEX)
+ 			phydev->duplex = DUPLEX_FULL;
+@@ -908,6 +981,35 @@ static int at803x_read_status(struct phy_device *phydev)
+ 		}
+ 	}
+ 
++	return 0;
++}
++
++static int at803x_read_status(struct phy_device *phydev)
++{
++	int err, old_link = phydev->link;
++
++	/* Update the link, but return if there was an error */
++	err = genphy_update_link(phydev);
++	if (err)
++		return err;
++
++	/* why bother the PHY if nothing can have changed */
++	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
++		return 0;
++
++	phydev->speed = SPEED_UNKNOWN;
++	phydev->duplex = DUPLEX_UNKNOWN;
++	phydev->pause = 0;
++	phydev->asym_pause = 0;
++
++	err = genphy_read_lpa(phydev);
++	if (err < 0)
++		return err;
++
++	err = at803x_read_specific_status(phydev);
++	if (err < 0)
++		return err;
++
+ 	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
+ 		phy_resolve_aneg_pause(phydev);
+ 
+@@ -955,7 +1057,30 @@ static int at803x_config_aneg(struct phy_device *phydev)
+ 			return ret;
+ 	}
+ 
+-	return genphy_config_aneg(phydev);
++	/* Do not restart auto-negotiation by setting ret to 0 defautly,
++	 * when calling __genphy_config_aneg later.
++	 */
++	ret = 0;
++
++	if (phydev->drv->phy_id == QCA8081_PHY_ID) {
++		int phy_ctrl = 0;
++
++		/* The reg MII_BMCR also needs to be configured for force mode, the
++		 * genphy_config_aneg is also needed.
++		 */
++		if (phydev->autoneg == AUTONEG_DISABLE)
++			genphy_c45_pma_setup_forced(phydev);
++
++		if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
++			phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
++
++		ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
++				MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
++		if (ret < 0)
++			return ret;
++	}
++
++	return __genphy_config_aneg(phydev, ret);
+ }
+ 
+ static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
+@@ -1040,6 +1165,15 @@ static int at803x_get_features(struct phy_device *phydev)
+ 	if (err)
+ 		return err;
+ 
++	if (at803x_match_phy_id(phydev, QCA8081_PHY_ID)) {
++		err = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE);
++		if (err < 0)
++			return err;
++
++		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported,
++				err & MDIO_PMA_NG_EXTABLE_2_5GBT);
++	}
++
+ 	if (!at803x_match_phy_id(phydev, ATH8031_PHY_ID))
+ 		return 0;
+ 
+@@ -1119,8 +1253,14 @@ static int at803x_cdt_start(struct phy_device *phydev, int pair)
+ {
+ 	u16 cdt;
+ 
+-	cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
+-	      AT803X_CDT_ENABLE_TEST;
++	/* qca8081 takes the different bit 15 to enable CDT test */
++	if (phydev->drv->phy_id == QCA8081_PHY_ID)
++		cdt = QCA808X_CDT_ENABLE_TEST |
++			QCA808X_CDT_LENGTH_UNIT |
++			QCA808X_CDT_INTER_CHECK_DIS;
++	else
++		cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
++			AT803X_CDT_ENABLE_TEST;
+ 
+ 	return phy_write(phydev, AT803X_CDT, cdt);
+ }
+@@ -1128,10 +1268,16 @@ static int at803x_cdt_start(struct phy_device *phydev, int pair)
+ static int at803x_cdt_wait_for_completion(struct phy_device *phydev)
+ {
+ 	int val, ret;
++	u16 cdt_en;
++
++	if (phydev->drv->phy_id == QCA8081_PHY_ID)
++		cdt_en = QCA808X_CDT_ENABLE_TEST;
++	else
++		cdt_en = AT803X_CDT_ENABLE_TEST;
+ 
+ 	/* One test run takes about 25ms */
+ 	ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
+-				    !(val & AT803X_CDT_ENABLE_TEST),
++				    !(val & cdt_en),
+ 				    30000, 100000, true);
+ 
+ 	return ret < 0 ? ret : 0;
+@@ -1338,6 +1484,339 @@ static int qca83xx_suspend(struct phy_device *phydev)
+ 	return 0;
+ }
+ 
++/**
++ * genphy_c45_fast_retrain - configure fast retrain registers
++ * @phydev: target phy_device struct
++ *
++ * Description: If fast-retrain is enabled, we configure PHY as
++ *   advertising fast retrain capable and THP Bypass Request, then
++ *   enable fast retrain. If it is not enabled, we configure fast
++ *   retrain disabled.
++ */
++#define MDIO_PMA_10GBR_FSRT_CSR	147	/* 10GBASE-R fast retrain status and control */
++/* PMA 10GBASE-R Fast Retrain status and control register. */
++#define MDIO_PMA_10GBR_FSRT_ENABLE	0x0001	/* Fast retrain enable */
++/* AN 10GBASE-T control register. */
++#define MDIO_AN_10GBT_CTRL_ADVFSRT2_5G	0x0020	/* Advertise 2.5GBASE-T fast retrain */
++#define MDIO_AN_CTRL2		64	/* AN THP bypass request control */
++/* AN MultiGBASE-T AN control 2 */
++#define MDIO_AN_THP_BP2_5GT	0x0008	/* 2.5GT THP bypass request */
++static int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable)
++{
++	int ret;
++
++	if (!enable)
++		return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FSRT_CSR,
++				MDIO_PMA_10GBR_FSRT_ENABLE);
++
++	if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported)) {
++		ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
++				MDIO_AN_10GBT_CTRL_ADVFSRT2_5G);
++		if (ret)
++			return ret;
++
++		ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_CTRL2,
++				MDIO_AN_THP_BP2_5GT);
++		if (ret)
++			return ret;
++	}
++
++	return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FSRT_CSR,
++			MDIO_PMA_10GBR_FSRT_ENABLE);
++}
++
++static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
++{
++	int ret;
++
++	/* Enable fast retrain */
++	ret = genphy_c45_fast_retrain(phydev, true);
++	if (ret)
++		return ret;
++
++	phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
++			QCA808X_TOP_OPTION1_DATA);
++	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
++			QCA808X_MSE_THRESHOLD_20DB_VALUE);
++	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
++			QCA808X_MSE_THRESHOLD_17DB_VALUE);
++	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
++			QCA808X_MSE_THRESHOLD_27DB_VALUE);
++	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
++			QCA808X_MSE_THRESHOLD_28DB_VALUE);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
++			QCA808X_MMD3_DEBUG_1_VALUE);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
++			QCA808X_MMD3_DEBUG_4_VALUE);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
++			QCA808X_MMD3_DEBUG_5_VALUE);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
++			QCA808X_MMD3_DEBUG_3_VALUE);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
++			QCA808X_MMD3_DEBUG_6_VALUE);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
++			QCA808X_MMD3_DEBUG_2_VALUE);
++
++	return 0;
++}
++
++static int qca808x_phy_ms_random_seed_set(struct phy_device *phydev)
++{
++	u16 seed_value = (prandom_u32() % QCA808X_MASTER_SLAVE_SEED_RANGE);
++
++	return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
++			QCA808X_MASTER_SLAVE_SEED_CFG,
++			FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value));
++}
++
++static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
++{
++	u16 seed_enable = 0;
++
++	if (enable)
++		seed_enable = QCA808X_MASTER_SLAVE_SEED_ENABLE;
++
++	return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
++			QCA808X_MASTER_SLAVE_SEED_ENABLE, seed_enable);
++}
++
++static int qca808x_config_init(struct phy_device *phydev)
++{
++	int ret;
++
++	/* Active adc&vga on 802.3az for the link 1000M and 100M */
++	ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
++			QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
++	if (ret)
++		return ret;
++
++	/* Adjust the threshold on 802.3az for the link 1000M */
++	ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
++			QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL);
++	if (ret)
++		return ret;
++
++	/* Config the fast retrain for the link 2500M */
++	ret = qca808x_phy_fast_retrain_config(phydev);
++	if (ret)
++		return ret;
++
++	/* Configure lower ramdom seed to make phy linked as slave mode */
++	ret = qca808x_phy_ms_random_seed_set(phydev);
++	if (ret)
++		return ret;
++
++	/* Enable seed */
++	ret = qca808x_phy_ms_seed_enable(phydev, true);
++	if (ret)
++		return ret;
++
++	/* Configure adc threshold as 100mv for the link 10M */
++	return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
++			QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
++}
++
++static int qca808x_read_status(struct phy_device *phydev)
++{
++	int ret;
++
++	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
++	if (ret < 0)
++		return ret;
++
++	linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
++			ret & MDIO_AN_10GBT_STAT_LP2_5G);
++
++	ret = genphy_read_status(phydev);
++	if (ret)
++		return ret;
++
++	ret = at803x_read_specific_status(phydev);
++	if (ret < 0)
++		return ret;
++
++	if (phydev->link && phydev->speed == SPEED_2500)
++		phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
++	else
++		phydev->interface = PHY_INTERFACE_MODE_SGMII;
++
++	/* generate seed as a lower random value to make PHY linked as SLAVE easily,
++	 * except for master/slave configuration fault detected.
++	 * the reason for not putting this code into the function link_change_notify is
++	 * the corner case where the link partner is also the qca8081 PHY and the seed
++	 * value is configured as the same value, the link can't be up and no link change
++	 * occurs.
++	 */
++	if (!phydev->link) {
++		if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
++			qca808x_phy_ms_seed_enable(phydev, false);
++		} else {
++			qca808x_phy_ms_random_seed_set(phydev);
++			qca808x_phy_ms_seed_enable(phydev, true);
++		}
++	}
++
++	return 0;
++}
++
++static int qca808x_soft_reset(struct phy_device *phydev)
++{
++	int ret;
++
++	ret = genphy_soft_reset(phydev);
++	if (ret < 0)
++		return ret;
++
++	return qca808x_phy_ms_seed_enable(phydev, true);
++}
++
++static bool qca808x_cdt_fault_length_valid(int cdt_code)
++{
++	switch (cdt_code) {
++	case QCA808X_CDT_STATUS_STAT_SHORT:
++	case QCA808X_CDT_STATUS_STAT_OPEN:
++		return true;
++	default:
++		return false;
++	}
++}
++
++static int qca808x_cable_test_result_trans(int cdt_code)
++{
++	switch (cdt_code) {
++	case QCA808X_CDT_STATUS_STAT_NORMAL:
++		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
++	case QCA808X_CDT_STATUS_STAT_SHORT:
++		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
++	case QCA808X_CDT_STATUS_STAT_OPEN:
++		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
++	case QCA808X_CDT_STATUS_STAT_FAIL:
++	default:
++		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
++	}
++}
++
++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair)
++{
++	int val;
++	u32 cdt_length_reg = 0;
++
++	switch (pair) {
++	case ETHTOOL_A_CABLE_PAIR_A:
++		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
++		break;
++	case ETHTOOL_A_CABLE_PAIR_B:
++		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
++		break;
++	case ETHTOOL_A_CABLE_PAIR_C:
++		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
++		break;
++	case ETHTOOL_A_CABLE_PAIR_D:
++		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
++	if (val < 0)
++		return val;
++
++	return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10;
++}
++
++static int qca808x_cable_test_start(struct phy_device *phydev)
++{
++	int ret;
++
++	/* perform CDT with the following configs:
++	 * 1. disable hibernation.
++	 * 2. force PHY working in MDI mode.
++	 * 3. for PHY working in 1000BaseT.
++	 * 4. configure the threshold.
++	 */
++
++	ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);
++	if (ret < 0)
++		return ret;
++
++	ret = at803x_config_mdix(phydev, ETH_TP_MDI);
++	if (ret < 0)
++		return ret;
++
++	/* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */
++	phydev->duplex = DUPLEX_FULL;
++	phydev->speed = SPEED_1000;
++	ret = genphy_c45_pma_setup_forced(phydev);
++	if (ret < 0)
++		return ret;
++
++	ret = genphy_setup_forced(phydev);
++	if (ret < 0)
++		return ret;
++
++	/* configure the thresholds for open, short, pair ok test */
++	phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
++	phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
++
++	return 0;
++}
++
++static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
++{
++	int ret, val;
++	int pair_a, pair_b, pair_c, pair_d;
++
++	*finished = false;
++
++	ret = at803x_cdt_start(phydev, 0);
++	if (ret)
++		return ret;
++
++	ret = at803x_cdt_wait_for_completion(phydev);
++	if (ret)
++		return ret;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
++	if (val < 0)
++		return val;
++
++	pair_a = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, val);
++	pair_b = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, val);
++	pair_c = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, val);
++	pair_d = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, val);
++
++	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
++				qca808x_cable_test_result_trans(pair_a));
++	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
++				qca808x_cable_test_result_trans(pair_b));
++	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
++				qca808x_cable_test_result_trans(pair_c));
++	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
++				qca808x_cable_test_result_trans(pair_d));
++
++	if (qca808x_cdt_fault_length_valid(pair_a))
++		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,
++				qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A));
++	if (qca808x_cdt_fault_length_valid(pair_b))
++		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B,
++				qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B));
++	if (qca808x_cdt_fault_length_valid(pair_c))
++		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C,
++				qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C));
++	if (qca808x_cdt_fault_length_valid(pair_d))
++		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D,
++				qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D));
++
++	*finished = true;
++
++	return 0;
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+ 	/* Qualcomm Atheros AR8035 */
+@@ -1482,6 +1961,26 @@ static struct phy_driver at803x_driver[] = {
+ 	.get_stats		= at803x_get_stats,
+ 	.suspend		= qca83xx_suspend,
+ 	.resume			= qca83xx_resume,
++}, {
++	/* Qualcomm QCA8081 */
++	PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
++	.name			= "Qualcomm QCA8081",
++	.flags			= PHY_POLL_CABLE_TEST,
++	.ack_interrupt		= &at803x_ack_interrupt,
++	.config_intr		= &at803x_config_intr,
++	.get_tunable		= at803x_get_tunable,
++	.set_tunable		= at803x_set_tunable,
++	.set_wol		= at803x_set_wol,
++	.get_wol		= at803x_get_wol,
++	.get_features		= at803x_get_features,
++	.config_aneg		= at803x_config_aneg,
++	.suspend		= genphy_suspend,
++	.resume			= genphy_resume,
++	.read_status		= qca808x_read_status,
++	.config_init		= qca808x_config_init,
++	.soft_reset		= qca808x_soft_reset,
++	.cable_test_start	= qca808x_cable_test_start,
++	.cable_test_get_status	= qca808x_cable_test_get_status,
+ }, };
+ 
+ module_phy_driver(at803x_driver);
+@@ -1495,6 +1994,7 @@ static struct mdio_device_id __maybe_unused atheros_tbl[] = {
+ 	{ PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
+ 	{ PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
+ 	{ PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
++	{ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
+ 	{ }
+ };
+ 
-- 
2.30.2


From 95ff35284d77e395c728e6a24888d298f648719c Mon Sep 17 00:00:00 2001
From: Serhii Serhieiev <adron@mstnt.com>
Date: Tue, 15 Feb 2022 07:35:09 +0200
Subject: [PATCH 10/11] mvebu: RB5009: remove sfp port from wan bridge

The presence of two bridges leads to a conflict between vlans
and Wan ports become inoperative.

Signed-off-by: Serhii Serhieiev <adron@mstnt.com>
---
 target/linux/mvebu/cortexa72/base-files/etc/board.d/02_network | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/target/linux/mvebu/cortexa72/base-files/etc/board.d/02_network b/target/linux/mvebu/cortexa72/base-files/etc/board.d/02_network
index 68cc8ff8cc..b96a33f13f 100644
--- a/target/linux/mvebu/cortexa72/base-files/etc/board.d/02_network
+++ b/target/linux/mvebu/cortexa72/base-files/etc/board.d/02_network
@@ -33,7 +33,7 @@ marvell,armada7040-db)
 	ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4 eth2" "eth0 eth1"
 	;;
 mikrotik,rb5009)
-	ucidef_set_interfaces_lan_wan "p2 p3 p4 p5 p6 p7 p8" "sfp p1"
+	ucidef_set_interfaces_lan_wan "p2 p3 p4 p5 p6 p7 p8 sfp" "p1"
 	;;
 *)
 	ucidef_set_interface_lan "eth0"
-- 
2.30.2


From d66ea2fe5189d83de21f2b852045940289c60988 Mon Sep 17 00:00:00 2001
From: Robert Marko <robimarko@gmail.com>
Date: Mon, 14 Mar 2022 18:55:25 +0100
Subject: [PATCH 11/11] mvebu: rb5009: add SFP GPIO pins

After finally being able to trace them out using a breakout lets add the
required SFP GPIO pins.

Signed-off-by: Robert Marko <robimarko@gmail.com>
---
 .../files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts b/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts
index 5535f8ad89..0babac6f08 100644
--- a/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts
+++ b/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-7040-rb5009.dts
@@ -70,6 +70,11 @@
 	sfp: sfp {
 		compatible = "sff,sfp";
 		i2c-bus = <&sfp_i2c>;
+		mod-def0-gpios = <&cp0_gpio1 11 GPIO_ACTIVE_LOW>;
+		los-gpios = <&cp0_gpio1 2 GPIO_ACTIVE_HIGH>;
+		tx-fault-gpios = <&cp0_gpio1 6 GPIO_ACTIVE_HIGH>;
+		tx-disable-gpios = <&cp0_gpio1 5 GPIO_ACTIVE_HIGH>;
+		rate-select0-gpios = <&cp0_gpio1 3 GPIO_ACTIVE_HIGH>;
 	};
 };
 
-- 
2.30.2

Raw