ipq40xx: add DSA switch driver
authorRobert Marko <robert.marko@sartura.hr>
Mon, 27 Dec 2021 14:11:18 +0000 (15:11 +0100)
committerDavid Bauer <mail@david-bauer.net>
Sun, 2 Oct 2022 21:04:38 +0000 (23:04 +0200)
Qualcomm IPQ40xx SoC-s have a variant of QCA8337N switch built-in.

It shares most of the stuff with its external counterpart, however it is
modified for the SoC.
Namely, it doesn't have second CPU port (Port 6), so it has 6 ports
instead of 7.
It also has no built-in PHY-s but rather requires external PSGMII based
companion PHY-s (QCA8072 and QCA8075) for which it first needs to carry
out calibration before using them.
PSGMII has a SoC built-in PHY that is used to connect to the PHY-s which
unfortunately requires some magic values as the datasheet doesnt document
the bits that are being set or the register at all.

Since its built-in it is MMIO like other peripherals and doesn't have its
own MDIO bus but depends on the SoC provided one.

CPU connection is at Port 0 and it uses some kind of a internal connection
and no traditional RGMII/SGMII.
It also doesn't use in-band tagging like other qca8k switches so a shinfo
based tagger is used.

This is based on the current OpenWrt qca8k version that has been imported
from generic target.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
18 files changed:
target/linux/ipq40xx/files/drivers/net/dsa/qca/qca8k-ipq4019.c
target/linux/ipq40xx/files/drivers/net/dsa/qca/qca8k-ipq4019.h
target/linux/ipq40xx/patches-5.10/705-net-dsa-add-Qualcomm-IPQ4019-built-in-switch-support.patch [new file with mode: 0644]
target/linux/ipq40xx/patches-5.10/706-arm-dts-ipq4019-add-switch-node.patch [new file with mode: 0644]
target/linux/ipq40xx/patches-5.10/706-dt-bindings-net-add-QCA807x-PHY.patch [deleted file]
target/linux/ipq40xx/patches-5.10/707-dt-bindings-net-add-QCA807x-PHY.patch [new file with mode: 0644]
target/linux/ipq40xx/patches-5.10/707-net-phy-Add-Qualcom-QCA807x-driver.patch [deleted file]
target/linux/ipq40xx/patches-5.10/708-arm-dts-ipq4019-QCA807x-properties.patch [deleted file]
target/linux/ipq40xx/patches-5.10/708-net-phy-Add-Qualcom-QCA807x-driver.patch [new file with mode: 0644]
target/linux/ipq40xx/patches-5.10/709-arm-dts-ipq4019-QCA807x-properties.patch [new file with mode: 0644]
target/linux/ipq40xx/patches-5.15/705-net-dsa-add-Qualcomm-IPQ4019-built-in-switch-support.patch [new file with mode: 0644]
target/linux/ipq40xx/patches-5.15/706-arm-dts-ipq4019-add-switch-node.patch [new file with mode: 0644]
target/linux/ipq40xx/patches-5.15/706-dt-bindings-net-add-QCA807x-PHY.patch [deleted file]
target/linux/ipq40xx/patches-5.15/707-dt-bindings-net-add-QCA807x-PHY.patch [new file with mode: 0644]
target/linux/ipq40xx/patches-5.15/707-net-phy-Add-Qualcom-QCA807x-driver.patch [deleted file]
target/linux/ipq40xx/patches-5.15/708-arm-dts-ipq4019-QCA807x-properties.patch [deleted file]
target/linux/ipq40xx/patches-5.15/708-net-phy-Add-Qualcom-QCA807x-driver.patch [new file with mode: 0644]
target/linux/ipq40xx/patches-5.15/709-arm-dts-ipq4019-QCA807x-properties.patch [new file with mode: 0644]

index fc8579d952f39407c3c3ee6be07972188c845afa..4d794262055da6d784d8c3226f9a8e8a5f23d675 100644 (file)
@@ -1,25 +1,28 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
- * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2011-2012, 2020-2021 Gabor Juhos <juhosg@openwrt.org>
  * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved.
  * Copyright (c) 2016 John Crispin <john@phrozen.org>
+ * Copyright (c) 2021 Robert Marko <robert.marko@sartura.hr>
  */
 
+#include <linux/version.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/mdio.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
-#include <linux/phy.h>
 #include <linux/netdevice.h>
-#include <net/dsa.h>
-#include <linux/of_net.h>
 #include <linux/of_mdio.h>
+#include <linux/of_net.h>
 #include <linux/of_platform.h>
-#include <linux/if_bridge.h>
-#include <linux/mdio.h>
+#include <linux/phy.h>
 #include <linux/phylink.h>
-#include <linux/gpio/consumer.h>
-#include <linux/etherdevice.h>
+#include <linux/reset.h>
+#include <net/dsa.h>
 
-#include "qca8k.h"
+#include "qca8k-ipq4019.h"
 
 #define MIB_DESC(_s, _o, _n)   \
        {                       \
@@ -68,186 +71,38 @@ static const struct qca8k_mib_desc ar8327_mib[] = {
        MIB_DESC(1, 0x9c, "TxExcDefer"),
        MIB_DESC(1, 0xa0, "TxDefer"),
        MIB_DESC(1, 0xa4, "TxLateCol"),
+       MIB_DESC(1, 0xa8, "RXUnicast"),
+       MIB_DESC(1, 0xac, "TXunicast"),
 };
 
-/* The 32bit switch registers are accessed indirectly. To achieve this we need
- * to set the page of the register. Track the last page that was set to reduce
- * mdio writes
- */
-static u16 qca8k_current_page = 0xffff;
-
-static void
-qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
-{
-       regaddr >>= 1;
-       *r1 = regaddr & 0x1e;
-
-       regaddr >>= 5;
-       *r2 = regaddr & 0x7;
-
-       regaddr >>= 3;
-       *page = regaddr & 0x3ff;
-}
-
-static int
-qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
-{
-       int ret;
-
-       ret = bus->read(bus, phy_id, regnum);
-       if (ret >= 0) {
-               *val = ret;
-               ret = bus->read(bus, phy_id, regnum + 1);
-               *val |= ret << 16;
-       }
-
-       if (ret < 0) {
-               dev_err_ratelimited(&bus->dev,
-                                   "failed to read qca8k 32bit register\n");
-               *val = 0;
-               return ret;
-       }
-
-       return 0;
-}
-
-static void
-qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
-{
-       u16 lo, hi;
-       int ret;
-
-       lo = val & 0xffff;
-       hi = (u16)(val >> 16);
-
-       ret = bus->write(bus, phy_id, regnum, lo);
-       if (ret >= 0)
-               ret = bus->write(bus, phy_id, regnum + 1, hi);
-       if (ret < 0)
-               dev_err_ratelimited(&bus->dev,
-                                   "failed to write qca8k 32bit register\n");
-}
-
-static int
-qca8k_set_page(struct mii_bus *bus, u16 page)
-{
-       int ret;
-
-       if (page == qca8k_current_page)
-               return 0;
-
-       ret = bus->write(bus, 0x18, 0, page);
-       if (ret < 0) {
-               dev_err_ratelimited(&bus->dev,
-                                   "failed to set qca8k page\n");
-               return ret;
-       }
-
-       qca8k_current_page = page;
-       usleep_range(1000, 2000);
-       return 0;
-}
-
 static int
 qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
 {
-       struct mii_bus *bus = priv->bus;
-       u16 r1, r2, page;
-       int ret;
-
-       qca8k_split_addr(reg, &r1, &r2, &page);
-
-       mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-
-       ret = qca8k_set_page(bus, page);
-       if (ret < 0)
-               goto exit;
-
-       ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val);
-
-exit:
-       mutex_unlock(&bus->mdio_lock);
-       return ret;
+       return regmap_read(priv->regmap, reg, val);
 }
 
 static int
 qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
 {
-       struct mii_bus *bus = priv->bus;
-       u16 r1, r2, page;
-       int ret;
-
-       qca8k_split_addr(reg, &r1, &r2, &page);
-
-       mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-
-       ret = qca8k_set_page(bus, page);
-       if (ret < 0)
-               goto exit;
-
-       qca8k_mii_write32(bus, 0x10 | r2, r1, val);
-
-exit:
-       mutex_unlock(&bus->mdio_lock);
-       return ret;
+       return regmap_write(priv->regmap, reg, val);
 }
 
 static int
 qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
 {
-       struct mii_bus *bus = priv->bus;
-       u16 r1, r2, page;
-       u32 val;
-       int ret;
-
-       qca8k_split_addr(reg, &r1, &r2, &page);
-
-       mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-
-       ret = qca8k_set_page(bus, page);
-       if (ret < 0)
-               goto exit;
-
-       ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
-       if (ret < 0)
-               goto exit;
-
-       val &= ~mask;
-       val |= write_val;
-       qca8k_mii_write32(bus, 0x10 | r2, r1, val);
-
-exit:
-       mutex_unlock(&bus->mdio_lock);
-
-       return ret;
+       return regmap_update_bits(priv->regmap, reg, mask, write_val);
 }
 
 static int
 qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val)
 {
-       return qca8k_rmw(priv, reg, 0, val);
+       return regmap_set_bits(priv->regmap, reg, val);
 }
 
 static int
 qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val)
 {
-       return qca8k_rmw(priv, reg, val, 0);
-}
-
-static int
-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
-{
-       struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
-
-       return qca8k_read(priv, reg, val);
-}
-
-static int
-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
-{
-       struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
-
-       return qca8k_write(priv, reg, val);
+       return regmap_clear_bits(priv->regmap, reg, val);
 }
 
 static const struct regmap_range qca8k_readable_ranges[] = {
@@ -274,33 +129,31 @@ static const struct regmap_access_table qca8k_readable_table = {
        .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
 };
 
-static struct regmap_config qca8k_regmap_config = {
-       .reg_bits = 16,
+static struct regmap_config qca8k_ipq4019_regmap_config = {
+       .reg_bits = 32,
        .val_bits = 32,
        .reg_stride = 4,
        .max_register = 0x16ac, /* end MIB - Port6 range */
-       .reg_read = qca8k_regmap_read,
-       .reg_write = qca8k_regmap_write,
        .rd_table = &qca8k_readable_table,
 };
 
+static struct regmap_config qca8k_ipq4019_psgmii_phy_regmap_config = {
+       .name = "psgmii-phy",
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .max_register = 0x7fc,
+};
+
 static int
 qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
 {
-       int ret, ret1;
        u32 val;
 
-       ret = read_poll_timeout(qca8k_read, ret1, !(val & mask),
-                               0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
-                               priv, reg, &val);
-
-       /* Check if qca8k_read has failed for a different reason
-        * before returning -ETIMEDOUT
-        */
-       if (ret < 0 && ret1 < 0)
-               return ret1;
-
-       return ret;
+       return regmap_read_poll_timeout(priv->regmap, reg, val,
+                                       !(val & mask),
+                                       0,
+                                       QCA8K_BUSY_WAIT_TIMEOUT);
 }
 
 static int
@@ -595,8 +448,11 @@ qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
 {
        u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
 
-       /* Port 0 and 6 have no internal PHY */
-       if (port > 0 && port < 6)
+       /* Port 0 is internally connected to the CPU
+        * TODO: Probably check for RGMII as well if it doesnt work
+        * in RGMII mode.
+        */
+       if (port > QCA8K_CPU_PORT)
                mask |= QCA8K_PORT_STATUS_LINK_AUTO;
 
        if (enable)
@@ -605,467 +461,56 @@ qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
                qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port), mask);
 }
 
-static u32
-qca8k_port_to_phy(int port)
-{
-       /* From Andrew Lunn:
-        * Port 0 has no internal phy.
-        * Port 1 has an internal PHY at MDIO address 0.
-        * Port 2 has an internal PHY at MDIO address 1.
-        * ...
-        * Port 5 has an internal PHY at MDIO address 4.
-        * Port 6 has no internal PHY.
-        */
-
-       return port - 1;
-}
-
-static int
-qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)
-{
-       u16 r1, r2, page;
-       u32 val;
-       int ret, ret1;
-
-       qca8k_split_addr(reg, &r1, &r2, &page);
-
-       ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0,
-                               QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
-                               bus, 0x10 | r2, r1, &val);
-
-       /* Check if qca8k_read has failed for a different reason
-        * before returnting -ETIMEDOUT
-        */
-       if (ret < 0 && ret1 < 0)
-               return ret1;
-
-       return ret;
-}
-
-static int
-qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data)
-{
-       u16 r1, r2, page;
-       u32 val;
-       int ret;
-
-       if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
-               return -EINVAL;
-
-       val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
-             QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
-             QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
-             QCA8K_MDIO_MASTER_DATA(data);
-
-       qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
-
-       mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-
-       ret = qca8k_set_page(bus, page);
-       if (ret)
-               goto exit;
-
-       qca8k_mii_write32(bus, 0x10 | r2, r1, val);
-
-       ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
-                                  QCA8K_MDIO_MASTER_BUSY);
-
-exit:
-       /* even if the busy_wait timeouts try to clear the MASTER_EN */
-       qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
-
-       mutex_unlock(&bus->mdio_lock);
-
-       return ret;
-}
-
-static int
-qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum)
-{
-       u16 r1, r2, page;
-       u32 val;
-       int ret;
-
-       if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
-               return -EINVAL;
-
-       val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
-             QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
-             QCA8K_MDIO_MASTER_REG_ADDR(regnum);
-
-       qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
-
-       mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-
-       ret = qca8k_set_page(bus, page);
-       if (ret)
-               goto exit;
-
-       qca8k_mii_write32(bus, 0x10 | r2, r1, val);
-
-       ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
-                                  QCA8K_MDIO_MASTER_BUSY);
-       if (ret)
-               goto exit;
-
-       ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
-
-exit:
-       /* even if the busy_wait timeouts try to clear the MASTER_EN */
-       qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
-
-       mutex_unlock(&bus->mdio_lock);
-
-       if (ret >= 0)
-               ret = val & QCA8K_MDIO_MASTER_DATA_MASK;
-
-       return ret;
-}
-
-static int
-qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data)
-{
-       struct qca8k_priv *priv = slave_bus->priv;
-       struct mii_bus *bus = priv->bus;
-
-       return qca8k_mdio_write(bus, phy, regnum, data);
-}
-
-static int
-qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum)
-{
-       struct qca8k_priv *priv = slave_bus->priv;
-       struct mii_bus *bus = priv->bus;
-
-       return qca8k_mdio_read(bus, phy, regnum);
-}
-
-static int
-qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data)
-{
-       struct qca8k_priv *priv = ds->priv;
-
-       /* Check if the legacy mapping should be used and the
-        * port is not correctly mapped to the right PHY in the
-        * devicetree
-        */
-       if (priv->legacy_phy_port_mapping)
-               port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
-
-       return qca8k_mdio_write(priv->bus, port, regnum, data);
-}
-
 static int
-qca8k_phy_read(struct dsa_switch *ds, int port, int regnum)
+qca8k_setup_port(struct dsa_switch *ds, int port)
 {
-       struct qca8k_priv *priv = ds->priv;
-       int ret;
-
-       /* Check if the legacy mapping should be used and the
-        * port is not correctly mapped to the right PHY in the
-        * devicetree
-        */
-       if (priv->legacy_phy_port_mapping)
-               port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
-
-       ret = qca8k_mdio_read(priv->bus, port, regnum);
-
-       if (ret < 0)
-               return 0xffff;
-
-       return ret;
-}
-
-static int
-qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio)
-{
-       struct dsa_switch *ds = priv->ds;
-       struct mii_bus *bus;
-
-       bus = devm_mdiobus_alloc(ds->dev);
-
-       if (!bus)
-               return -ENOMEM;
-
-       bus->priv = (void *)priv;
-       bus->name = "qca8k slave mii";
-       bus->read = qca8k_internal_mdio_read;
-       bus->write = qca8k_internal_mdio_write;
-       snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d",
-                ds->index);
-
-       bus->parent = ds->dev;
-       bus->phy_mask = ~ds->phys_mii_mask;
-
-       ds->slave_mii_bus = bus;
-
-       return devm_of_mdiobus_register(priv->dev, bus, mdio);
-}
-
-static int
-qca8k_setup_mdio_bus(struct qca8k_priv *priv)
-{
-       u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg;
-       struct device_node *ports, *port, *mdio;
-       phy_interface_t mode;
-       int err;
-
-       ports = of_get_child_by_name(priv->dev->of_node, "ports");
-       if (!ports)
-               ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports");
-
-       if (!ports)
-               return -EINVAL;
-
-       for_each_available_child_of_node(ports, port) {
-               err = of_property_read_u32(port, "reg", &reg);
-               if (err) {
-                       of_node_put(port);
-                       of_node_put(ports);
-                       return err;
-               }
-
-               if (!dsa_is_user_port(priv->ds, reg))
-                       continue;
-
-               of_get_phy_mode(port, &mode);
-
-               if (of_property_read_bool(port, "phy-handle") &&
-                   mode != PHY_INTERFACE_MODE_INTERNAL)
-                       external_mdio_mask |= BIT(reg);
-               else
-                       internal_mdio_mask |= BIT(reg);
-       }
-
-       of_node_put(ports);
-       if (!external_mdio_mask && !internal_mdio_mask) {
-               dev_err(priv->dev, "no PHYs are defined.\n");
-               return -EINVAL;
-       }
-
-       /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
-        * the MDIO_MASTER register also _disconnects_ the external MDC
-        * passthrough to the internal PHYs. It's not possible to use both
-        * configurations at the same time!
-        *
-        * Because this came up during the review process:
-        * If the external mdio-bus driver is capable magically disabling
-        * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's
-        * accessors for the time being, it would be possible to pull this
-        * off.
-        */
-       if (!!external_mdio_mask && !!internal_mdio_mask) {
-               dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
-               return -EINVAL;
-       }
-
-       if (external_mdio_mask) {
-               /* Make sure to disable the internal mdio bus in cases
-                * a dt-overlay and driver reload changed the configuration
-                */
-
-               return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
-                                      QCA8K_MDIO_MASTER_EN);
-       }
-
-       /* Check if the devicetree declare the port:phy mapping */
-       mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
-       if (of_device_is_available(mdio)) {
-               err = qca8k_mdio_register(priv, mdio);
-               if (err)
-                       of_node_put(mdio);
-
-               return err;
-       }
-
-       /* If a mapping can't be found the legacy mapping is used,
-        * using the qca8k_port_to_phy function
-        */
-       priv->legacy_phy_port_mapping = true;
-       priv->ops.phy_read = qca8k_phy_read;
-       priv->ops.phy_write = qca8k_phy_write;
-
-       return 0;
-}
-
-static int
-qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
-{
-       u32 mask = 0;
-       int ret = 0;
-
-       /* SoC specific settings for ipq8064.
-        * If more device require this consider adding
-        * a dedicated binding.
-        */
-       if (of_machine_is_compatible("qcom,ipq8064"))
-               mask |= QCA8K_MAC_PWR_RGMII0_1_8V;
-
-       /* SoC specific settings for ipq8065 */
-       if (of_machine_is_compatible("qcom,ipq8065"))
-               mask |= QCA8K_MAC_PWR_RGMII1_1_8V;
-
-       if (mask) {
-               ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL,
-                               QCA8K_MAC_PWR_RGMII0_1_8V |
-                               QCA8K_MAC_PWR_RGMII1_1_8V,
-                               mask);
-       }
-
-       return ret;
-}
-
-static int qca8k_find_cpu_port(struct dsa_switch *ds)
-{
-       struct qca8k_priv *priv = ds->priv;
-
-       /* Find the connected cpu port. Valid port are 0 or 6 */
-       if (dsa_is_cpu_port(ds, 0))
-               return 0;
-
-       dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
-
-       if (dsa_is_cpu_port(ds, 6))
-               return 6;
-
-       return -EINVAL;
-}
-
-static int
-qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
-{
-       struct device_node *node = priv->dev->of_node;
-       const struct qca8k_match_data *data;
-       u32 val = 0;
+       struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
        int ret;
 
-       /* QCA8327 require to set to the correct mode.
-        * His bigger brother QCA8328 have the 172 pin layout.
-        * Should be applied by default but we set this just to make sure.
-        */
-       if (priv->switch_id == QCA8K_ID_QCA8327) {
-               data = of_device_get_match_data(priv->dev);
-
-               /* Set the correct package of 148 pin for QCA8327 */
-               if (data->reduced_package)
-                       val |= QCA8327_PWS_PACKAGE148_EN;
-
-               ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
-                               val);
+       /* CPU port gets connected to all user ports of the switch */
+       if (dsa_is_cpu_port(ds, port)) {
+               ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT),
+                               QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
                if (ret)
                        return ret;
-       }
-
-       if (of_property_read_bool(node, "qca,ignore-power-on-sel"))
-               val |= QCA8K_PWS_POWER_ON_SEL;
-
-       if (of_property_read_bool(node, "qca,led-open-drain")) {
-               if (!(val & QCA8K_PWS_POWER_ON_SEL)) {
-                       dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set.");
-                       return -EINVAL;
-               }
 
-               val |= QCA8K_PWS_LED_OPEN_EN_CSR;
+               /* Disable CPU ARP Auto-learning by default */
+               ret = qca8k_reg_clear(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT),
+                                     QCA8K_PORT_LOOKUP_LEARN);
+               if (ret)
+                       return ret;
        }
 
-       return qca8k_rmw(priv, QCA8K_REG_PWS,
-                       QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL,
-                       val);
-}
-
-static int
-qca8k_parse_port_config(struct qca8k_priv *priv)
-{
-       int port, cpu_port_index = -1, ret;
-       struct device_node *port_dn;
-       phy_interface_t mode;
-       struct dsa_port *dp;
-       u32 delay;
-
-       /* We have 2 CPU port. Check them */
-       for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) {
-               /* Skip every other port */
-               if (port != 0 && port != 6)
-                       continue;
-
-               dp = dsa_to_port(priv->ds, port);
-               port_dn = dp->dn;
-               cpu_port_index++;
+       /* Individual user ports get connected to CPU port only */
+       if (dsa_is_user_port(ds, port)) {
+               int shift = 16 * (port % 2);
 
-               if (!of_device_is_available(port_dn))
-                       continue;
+               ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+                               QCA8K_PORT_LOOKUP_MEMBER,
+                               BIT(QCA8K_CPU_PORT));
+               if (ret)
+                       return ret;
 
-               ret = of_get_phy_mode(port_dn, &mode);
+               /* Enable ARP Auto-learning by default */
+               ret = qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+                                   QCA8K_PORT_LOOKUP_LEARN);
                if (ret)
-                       continue;
+                       return ret;
 
-               switch (mode) {
-               case PHY_INTERFACE_MODE_RGMII:
-               case PHY_INTERFACE_MODE_RGMII_ID:
-               case PHY_INTERFACE_MODE_RGMII_TXID:
-               case PHY_INTERFACE_MODE_RGMII_RXID:
-               case PHY_INTERFACE_MODE_SGMII:
-                       delay = 0;
-
-                       if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay))
-                               /* Switch regs accept value in ns, convert ps to ns */
-                               delay = delay / 1000;
-                       else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
-                                mode == PHY_INTERFACE_MODE_RGMII_TXID)
-                               delay = 1;
-
-                       if (delay > QCA8K_MAX_DELAY) {
-                               dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
-                               delay = 3;
-                       }
-
-                       priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay;
-
-                       delay = 0;
-
-                       if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay))
-                               /* Switch regs accept value in ns, convert ps to ns */
-                               delay = delay / 1000;
-                       else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
-                                mode == PHY_INTERFACE_MODE_RGMII_RXID)
-                               delay = 2;
-
-                       if (delay > QCA8K_MAX_DELAY) {
-                               dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
-                               delay = 3;
-                       }
-
-                       priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay;
-
-                       /* Skip sgmii parsing for rgmii* mode */
-                       if (mode == PHY_INTERFACE_MODE_RGMII ||
-                           mode == PHY_INTERFACE_MODE_RGMII_ID ||
-                           mode == PHY_INTERFACE_MODE_RGMII_TXID ||
-                           mode == PHY_INTERFACE_MODE_RGMII_RXID)
-                               break;
-
-                       if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
-                               priv->ports_config.sgmii_tx_clk_falling_edge = true;
-
-                       if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
-                               priv->ports_config.sgmii_rx_clk_falling_edge = true;
-
-                       if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) {
-                               priv->ports_config.sgmii_enable_pll = true;
-
-                               if (priv->switch_id == QCA8K_ID_QCA8327) {
-                                       dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling");
-                                       priv->ports_config.sgmii_enable_pll = false;
-                               }
-
-                               if (priv->switch_revision < 2)
-                                       dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more.");
-                       }
+               /* For port based vlans to work we need to set the
+                * default egress vid
+                */
+               ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
+                               0xfff << shift,
+                               QCA8K_PORT_VID_DEF << shift);
+               if (ret)
+                       return ret;
 
-                       break;
-               default:
-                       continue;
-               }
+               ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
+                                 QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
+                                 QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
+               if (ret)
+                       return ret;
        }
 
        return 0;
@@ -1075,40 +520,14 @@ static int
 qca8k_setup(struct dsa_switch *ds)
 {
        struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-       int cpu_port, ret, i;
-       u32 mask;
+       int ret, i;
 
-       cpu_port = qca8k_find_cpu_port(ds);
-       if (cpu_port < 0) {
-               dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
-               return cpu_port;
+       /* Make sure that port 0 is the cpu port */
+       if (!dsa_is_cpu_port(ds, 0)) {
+               dev_err(priv->dev, "port 0 is not the CPU port");
+               return -EINVAL;
        }
 
-       /* Parse CPU port config to be later used in phy_link mac_config */
-       ret = qca8k_parse_port_config(priv);
-       if (ret)
-               return ret;
-
-       mutex_init(&priv->reg_mutex);
-
-       /* Start by setting up the register mapping */
-       priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
-                                       &qca8k_regmap_config);
-       if (IS_ERR(priv->regmap))
-               dev_warn(priv->dev, "regmap initialization failed");
-
-       ret = qca8k_setup_mdio_bus(priv);
-       if (ret)
-               return ret;
-
-       ret = qca8k_setup_of_pws_reg(priv);
-       if (ret)
-               return ret;
-
-       ret = qca8k_setup_mac_pwr_sel(priv);
-       if (ret)
-               return ret;
-
        /* Enable CPU Port */
        ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
                            QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
@@ -1120,149 +539,53 @@ qca8k_setup(struct dsa_switch *ds)
        /* Enable MIB counters */
        ret = qca8k_mib_init(priv);
        if (ret)
-               dev_warn(priv->dev, "mib init failed");
+               dev_warn(priv->dev, "MIB init failed");
+
+       /* Enable QCA header mode on the cpu port */
+       ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT),
+                         QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+                         QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
+       if (ret) {
+               dev_err(priv->dev, "failed enabling QCA header mode");
+               return ret;
+       }
 
-       /* Initial setup of all ports */
+       /* Disable forwarding by default on all ports */
        for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-               /* Disable forwarding by default on all ports */
                ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
                                QCA8K_PORT_LOOKUP_MEMBER, 0);
                if (ret)
                        return ret;
-
-               /* Enable QCA header mode on all cpu ports */
-               if (dsa_is_cpu_port(ds, i)) {
-                       ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
-                                         QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
-                                         QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
-                       if (ret) {
-                               dev_err(priv->dev, "failed enabling QCA header mode");
-                               return ret;
-                       }
-               }
-
-               /* Disable MAC by default on all user ports */
-               if (dsa_is_user_port(ds, i))
-                       qca8k_port_set_status(priv, i, 0);
        }
 
-       /* Forward all unknown frames to CPU port for Linux processing
-        * Notice that in multi-cpu config only one port should be set
-        * for igmp, unknown, multicast and broadcast packet
-        */
+       /* Disable MAC by default on all ports */
+       for (i = 1; i < QCA8K_NUM_PORTS; i++)
+               qca8k_port_set_status(priv, i, 0);
+
+       /* Forward all unknown frames to CPU port for Linux processing */
        ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
-                         BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
-                         BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
-                         BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
-                         BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
+                         BIT(QCA8K_CPU_PORT) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+                         BIT(QCA8K_CPU_PORT) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+                         BIT(QCA8K_CPU_PORT) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
+                         BIT(QCA8K_CPU_PORT) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
        if (ret)
                return ret;
 
-       /* Setup connection between CPU port & user ports
-        * Configure specific switch configuration for ports
-        */
+       /* Setup connection between CPU port & user ports */
        for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-               /* CPU port gets connected to all user ports of the switch */
-               if (dsa_is_cpu_port(ds, i)) {
-                       ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
-                                       QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
-                       if (ret)
-                               return ret;
-               }
-
-               /* Individual user ports get connected to CPU port only */
-               if (dsa_is_user_port(ds, i)) {
-                       int shift = 16 * (i % 2);
-
-                       ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
-                                       QCA8K_PORT_LOOKUP_MEMBER,
-                                       BIT(cpu_port));
-                       if (ret)
-                               return ret;
-
-                       /* Enable ARP Auto-learning by default */
-                       ret = qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i),
-                                           QCA8K_PORT_LOOKUP_LEARN);
-                       if (ret)
-                               return ret;
-
-                       /* For port based vlans to work we need to set the
-                        * default egress vid
-                        */
-                       ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
-                                       0xfff << shift,
-                                       QCA8K_PORT_VID_DEF << shift);
-                       if (ret)
-                               return ret;
-
-                       ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
-                                         QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
-                                         QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
-                       if (ret)
-                               return ret;
-               }
-
-               /* The port 5 of the qca8337 have some problem in flood condition. The
-                * original legacy driver had some specific buffer and priority settings
-                * for the different port suggested by the QCA switch team. Add this
-                * missing settings to improve switch stability under load condition.
-                * This problem is limited to qca8337 and other qca8k switch are not affected.
-                */
-               if (priv->switch_id == QCA8K_ID_QCA8337) {
-                       switch (i) {
-                       /* The 2 CPU port and port 5 requires some different
-                        * priority than any other ports.
-                        */
-                       case 0:
-                       case 5:
-                       case 6:
-                               mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
-                               break;
-                       default:
-                               mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
-                                       QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
-                       }
-                       qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
-
-                       mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
-                       QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
-                       QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
-                       QCA8K_PORT_HOL_CTRL1_WRED_EN;
-                       qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
-                                 QCA8K_PORT_HOL_CTRL1_ING_BUF |
-                                 QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
-                                 QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
-                                 QCA8K_PORT_HOL_CTRL1_WRED_EN,
-                                 mask);
-               }
-
-               /* Set initial MTU for every port.
-                * We have only have a general MTU setting. So track
-                * every port and set the max across all port.
-                */
-               priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
-       }
-
-       /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
-       if (priv->switch_id == QCA8K_ID_QCA8327) {
-               mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
-                      QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
-               qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
-                         QCA8K_GLOBAL_FC_GOL_XON_THRES_S |
-                         QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S,
-                         mask);
+               ret = qca8k_setup_port(ds, i);
+               if (ret)
+                       return ret;
        }
 
        /* Setup our port MTUs to match power on defaults */
+       for (i = 0; i < QCA8K_NUM_PORTS; i++)
+               /* Set per port MTU to 1500 as the MTU change function
+                * will add the overhead and if its set to 1518 then it
+                * will apply the overhead again and we will end up with
+                * MTU of 1536 instead of 1518
+                */
+               priv->port_mtu[i] = ETH_DATA_LEN;
        ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
        if (ret)
                dev_warn(priv->dev, "failed setting MTU settings");
@@ -1273,48 +596,87 @@ qca8k_setup(struct dsa_switch *ds)
        /* We don't have interrupts for link changes, so we need to poll */
        ds->pcs_poll = true;
 
+       /* CPU port HW learning doesnt work correctly, so let DSA handle it */
+       ds->assisted_learning_on_cpu_port = true;
+
        return 0;
 }
 
-static void
-qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index,
-                                     u32 reg)
+static int psgmii_vco_calibrate(struct dsa_switch *ds)
 {
-       u32 delay, val = 0;
-       int ret;
+       struct qca8k_priv *priv = ds->priv;
+       int val, ret;
 
-       /* Delay can be declared in 3 different way.
-        * Mode to rgmii and internal-delay standard binding defined
-        * rgmii-id or rgmii-tx/rx phy mode set.
-        * The parse logic set a delay different than 0 only when one
-        * of the 3 different way is used. In all other case delay is
-        * not enabled. With ID or TX/RXID delay is enabled and set
-        * to the default and recommended value.
-        */
-       if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) {
-               delay = priv->ports_config.rgmii_tx_delay[cpu_port_index];
+       if (!priv->psgmii_ethphy) {
+               dev_err(ds->dev, "PSGMII eth PHY missing, calibration failed!\n");
+               return -ENODEV;
+       }
 
-               val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
-                       QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
+       /* Fix PSGMII RX 20bit */
+       ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x5b);
+       /* Reset PSGMII PHY */
+       ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x1b);
+       /* Release reset */
+       ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x5b);
+
+       /* Poll for VCO PLL calibration finish */
+       ret = phy_read_mmd_poll_timeout(priv->psgmii_ethphy,
+                                       MDIO_MMD_PMAPMD,
+                                       0x28, val,
+                                       (val & BIT(0)),
+                                       10000, 1000000,
+                                       false);
+       if (ret) {
+               dev_err(ds->dev, "QCA807x PSGMII VCO calibration PLL not ready\n");
+               return ret;
        }
 
-       if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) {
-               delay = priv->ports_config.rgmii_rx_delay[cpu_port_index];
+       /* Freeze PSGMII RX CDR */
+       ret = phy_write(priv->psgmii_ethphy, MII_RESV2, 0x2230);
 
-               val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
-                       QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
+       /* Start PSGMIIPHY VCO PLL calibration */
+       ret = regmap_set_bits(priv->psgmii,
+                       PSGMIIPHY_VCO_CALIBRATION_CONTROL_REGISTER_1,
+                       PSGMIIPHY_REG_PLL_VCO_CALIB_RESTART);
+
+       /* Poll for PSGMIIPHY PLL calibration finish */
+       ret = regmap_read_poll_timeout(priv->psgmii,
+                                      PSGMIIPHY_VCO_CALIBRATION_CONTROL_REGISTER_2,
+                                      val, val & PSGMIIPHY_REG_PLL_VCO_CALIB_READY,
+                                      10000, 1000000);
+       if (ret) {
+               dev_err(ds->dev, "PSGMIIPHY VCO calibration PLL not ready\n");
+               return ret;
        }
 
-       /* Set RGMII delay based on the selected values */
-       ret = qca8k_rmw(priv, reg,
-                       QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK |
-                       QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK |
-                       QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
-                       QCA8K_PORT_PAD_RGMII_RX_DELAY_EN,
-                       val);
-       if (ret)
-               dev_err(priv->dev, "Failed to set internal delay for CPU port%d",
-                       cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6);
+       /* Release PSGMII RX CDR */
+       ret = phy_write(priv->psgmii_ethphy, MII_RESV2, 0x3230);
+
+       /* Release PSGMII RX 20bit */
+       ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x5f);
+
+       return ret;
+}
+
+static int ipq4019_psgmii_configure(struct dsa_switch *ds)
+{
+       struct qca8k_priv *priv = ds->priv;
+       int ret;
+
+       if (!priv->psgmii_calibrated) {
+               ret = psgmii_vco_calibrate(ds);
+
+               ret = regmap_clear_bits(priv->psgmii, PSGMIIPHY_MODE_CONTROL,
+                                       PSGMIIPHY_MODE_ATHR_CSCO_MODE_25M);
+               ret = regmap_write(priv->psgmii, PSGMIIPHY_TX_CONTROL,
+                                  PSGMIIPHY_TX_CONTROL_MAGIC_VALUE);
+
+               priv->psgmii_calibrated = true;
+
+               return ret;
+       }
+
+       return 0;
 }
 
 static void
@@ -1322,141 +684,33 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
                         const struct phylink_link_state *state)
 {
        struct qca8k_priv *priv = ds->priv;
-       int cpu_port_index, ret;
-       u32 reg, val;
 
        switch (port) {
-       case 0: /* 1st CPU port */
-               if (state->interface != PHY_INTERFACE_MODE_RGMII &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
-                   state->interface != PHY_INTERFACE_MODE_SGMII)
-                       return;
-
-               reg = QCA8K_REG_PORT0_PAD_CTRL;
-               cpu_port_index = QCA8K_CPU_PORT0;
-               break;
+       case 0:
+               /* CPU port, no configuration needed */
+               return;
        case 1:
        case 2:
        case 3:
+               if (state->interface == PHY_INTERFACE_MODE_PSGMII)
+                       if (ipq4019_psgmii_configure(ds))
+                               dev_err(ds->dev, "PSGMII configuration failed!\n");
+               return;
        case 4:
        case 5:
-               /* Internal PHY, nothing to do */
-               return;
-       case 6: /* 2nd CPU port / external PHY */
-               if (state->interface != PHY_INTERFACE_MODE_RGMII &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
-                   state->interface != PHY_INTERFACE_MODE_SGMII &&
-                   state->interface != PHY_INTERFACE_MODE_1000BASEX)
-                       return;
-
-               reg = QCA8K_REG_PORT6_PAD_CTRL;
-               cpu_port_index = QCA8K_CPU_PORT6;
-               break;
-       default:
-               dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
-               return;
-       }
-
-       if (port != 6 && phylink_autoneg_inband(mode)) {
-               dev_err(ds->dev, "%s: in-band negotiation unsupported\n",
-                       __func__);
-               return;
-       }
-
-       switch (state->interface) {
-       case PHY_INTERFACE_MODE_RGMII:
-       case PHY_INTERFACE_MODE_RGMII_ID:
-       case PHY_INTERFACE_MODE_RGMII_TXID:
-       case PHY_INTERFACE_MODE_RGMII_RXID:
-               qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
-
-               /* Configure rgmii delay */
-               qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
-
-               /* QCA8337 requires to set rgmii rx delay for all ports.
-                * This is enabled through PORT5_PAD_CTRL for all ports,
-                * rather than individual port registers.
-                */
-               if (priv->switch_id == QCA8K_ID_QCA8337)
-                       qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
-                                   QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
-               break;
-       case PHY_INTERFACE_MODE_SGMII:
-       case PHY_INTERFACE_MODE_1000BASEX:
-               /* Enable SGMII on the port */
-               qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
-
-               /* Enable/disable SerDes auto-negotiation as necessary */
-               ret = qca8k_read(priv, QCA8K_REG_PWS, &val);
-               if (ret)
-                       return;
-               if (phylink_autoneg_inband(mode))
-                       val &= ~QCA8K_PWS_SERDES_AEN_DIS;
-               else
-                       val |= QCA8K_PWS_SERDES_AEN_DIS;
-               qca8k_write(priv, QCA8K_REG_PWS, val);
-
-               /* Configure the SGMII parameters */
-               ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
-               if (ret)
-                       return;
-
-               val |= QCA8K_SGMII_EN_SD;
-
-               if (priv->ports_config.sgmii_enable_pll)
-                       val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
-                              QCA8K_SGMII_EN_TX;
-
-               if (dsa_is_cpu_port(ds, port)) {
-                       /* CPU port, we're talking to the CPU MAC, be a PHY */
-                       val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
-                       val |= QCA8K_SGMII_MODE_CTRL_PHY;
-               } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
-                       val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
-                       val |= QCA8K_SGMII_MODE_CTRL_MAC;
-               } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
-                       val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
-                       val |= QCA8K_SGMII_MODE_CTRL_BASEX;
+               if (state->interface == PHY_INTERFACE_MODE_RGMII ||
+                   state->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+                   state->interface == PHY_INTERFACE_MODE_RGMII_RXID ||
+                   state->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+                       qca8k_reg_set(priv, QCA8K_REG_RGMII_CTRL, QCA8K_RGMII_CTRL_CLK);
                }
 
-               qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
-
-               /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
-                * falling edge is set writing in the PORT0 PAD reg
-                */
-               if (priv->switch_id == QCA8K_ID_QCA8327 ||
-                   priv->switch_id == QCA8K_ID_QCA8337)
-                       reg = QCA8K_REG_PORT0_PAD_CTRL;
-
-               val = 0;
-
-               /* SGMII Clock phase configuration */
-               if (priv->ports_config.sgmii_rx_clk_falling_edge)
-                       val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE;
-
-               if (priv->ports_config.sgmii_tx_clk_falling_edge)
-                       val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
-
-               if (val)
-                       ret = qca8k_rmw(priv, reg,
-                                       QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
-                                       QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
-                                       val);
-
-               /* From original code is reported port instability as SGMII also
-                * require delay set. Apply advised values here or take them from DT.
-                */
-               if (state->interface == PHY_INTERFACE_MODE_SGMII)
-                       qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
-
-               break;
+               if (state->interface == PHY_INTERFACE_MODE_PSGMII)
+                       if (ipq4019_psgmii_configure(ds))
+                               dev_err(ds->dev, "PSGMII configuration failed!\n");
+               return;
        default:
-               dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
-                       phy_modes(state->interface), port);
+               dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
                return;
        }
 }
@@ -1469,59 +723,49 @@ qca8k_phylink_validate(struct dsa_switch *ds, int port,
        __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 
        switch (port) {
-       case 0: /* 1st CPU port */
-               if (state->interface != PHY_INTERFACE_MODE_NA &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
-                   state->interface != PHY_INTERFACE_MODE_SGMII)
+       case 0: /* CPU port */
+               if (state->interface != PHY_INTERFACE_MODE_INTERNAL)
                        goto unsupported;
                break;
        case 1:
        case 2:
        case 3:
-       case 4:
-       case 5:
-               /* Internal PHY */
-               if (state->interface != PHY_INTERFACE_MODE_NA &&
-                   state->interface != PHY_INTERFACE_MODE_GMII &&
-                   state->interface != PHY_INTERFACE_MODE_INTERNAL)
+               /* Only PSGMII mode is supported */
+               if (state->interface != PHY_INTERFACE_MODE_PSGMII)
                        goto unsupported;
                break;
-       case 6: /* 2nd CPU port / external PHY */
-               if (state->interface != PHY_INTERFACE_MODE_NA &&
+       case 4:
+       case 5:
+               /* PSGMII and RGMII modes are supported */
+               if (state->interface != PHY_INTERFACE_MODE_PSGMII &&
                    state->interface != PHY_INTERFACE_MODE_RGMII &&
                    state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
-                   state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
                    state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
-                   state->interface != PHY_INTERFACE_MODE_SGMII &&
-                   state->interface != PHY_INTERFACE_MODE_1000BASEX)
+                   state->interface != PHY_INTERFACE_MODE_RGMII_TXID)
                        goto unsupported;
                break;
        default:
 unsupported:
+               dev_warn(ds->dev, "interface '%s' (%d) on port %d is not supported\n",
+                        phy_modes(state->interface), state->interface, port);
                linkmode_zero(supported);
                return;
        }
 
-       phylink_set_port_modes(mask);
-       phylink_set(mask, Autoneg);
-
-       phylink_set(mask, 1000baseT_Full);
-       phylink_set(mask, 10baseT_Half);
-       phylink_set(mask, 10baseT_Full);
-       phylink_set(mask, 100baseT_Half);
-       phylink_set(mask, 100baseT_Full);
+       if (port == 0) {
+               phylink_set_port_modes(mask);
 
-       if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
-               phylink_set(mask, 1000baseX_Full);
+               phylink_set(mask, 1000baseT_Full);
 
-       phylink_set(mask, Pause);
-       phylink_set(mask, Asym_Pause);
+               phylink_set(mask, Pause);
+               phylink_set(mask, Asym_Pause);
 
-       linkmode_and(supported, supported, mask);
-       linkmode_and(state->advertising, state->advertising, mask);
+               linkmode_and(supported, supported, mask);
+               linkmode_and(state->advertising, state->advertising, mask);
+       } else {
+               /* Simply copy what PHYs tell us */
+               linkmode_copy(state->advertising, supported);
+       }
 }
 
 static int
@@ -1895,14 +1139,22 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
        return 0;
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
 static int
 qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
                          struct switchdev_trans *trans)
+#else
+static int
+qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+                          struct netlink_ext_ack *extack)
+#endif
 {
        struct qca8k_priv *priv = ds->priv;
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
        if (switchdev_trans_ph_prepare(trans))
                return 0;
+#endif
 
        if (vlan_filtering) {
                qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
@@ -1917,39 +1169,70 @@ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
        return 0;
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
 static int
 qca8k_port_vlan_prepare(struct dsa_switch *ds, int port,
                        const struct switchdev_obj_port_vlan *vlan)
 {
        return 0;
 }
+#endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
 static void
 qca8k_port_vlan_add(struct dsa_switch *ds, int port,
                    const struct switchdev_obj_port_vlan *vlan)
+#else
+static int
+qca8k_port_vlan_add(struct dsa_switch *ds, int port,
+                   const struct switchdev_obj_port_vlan *vlan,
+                   struct netlink_ext_ack *extack)
+#endif
 {
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
        struct qca8k_priv *priv = ds->priv;
        int ret = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
        u16 vid;
 
        for (vid = vlan->vid_begin; vid <= vlan->vid_end && !ret; ++vid)
                ret = qca8k_vlan_add(priv, port, vid, untagged);
-
+#else
+       ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
        if (ret)
                dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
+#else
+       if (ret) {
+               dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
+               return ret;
+       }
+#endif
 
        if (pvid) {
                int shift = 16 * (port % 2);
 
                qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
                          0xfff << shift,
                          vlan->vid_end << shift);
+#else
+                         0xfff << shift, vlan->vid << shift);
+#endif
                qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
                            QCA8K_PORT_VLAN_CVID(vlan->vid_end) |
                            QCA8K_PORT_VLAN_SVID(vlan->vid_end));
+#else
+                           QCA8K_PORT_VLAN_CVID(vlan->vid) |
+                           QCA8K_PORT_VLAN_SVID(vlan->vid));
+#endif
        }
+#if LINUX_VERSION_CODE > KERNEL_VERSION(5,12,0)
+       return 0;
+#endif
 }
 
 static int
@@ -1958,38 +1241,25 @@ qca8k_port_vlan_del(struct dsa_switch *ds, int port,
 {
        struct qca8k_priv *priv = ds->priv;
        int ret = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
        u16 vid;
 
        for (vid = vlan->vid_begin; vid <= vlan->vid_end && !ret; ++vid)
                ret = qca8k_vlan_del(priv, port, vid);
-
+#else
+       ret = qca8k_vlan_del(priv, port, vlan->vid);
+#endif
        if (ret)
                dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
 
        return ret;
 }
 
-static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
-{
-       struct qca8k_priv *priv = ds->priv;
-
-       /* Communicate to the phy internal driver the switch revision.
-        * Based on the switch revision different values needs to be
-        * set to the dbg and mmd reg on the phy.
-        * The first 2 bit are used to communicate the switch revision
-        * to the phy driver.
-        */
-       if (port > 0 && port < 6)
-               return priv->switch_revision;
-
-       return 0;
-}
-
 static enum dsa_tag_protocol
 qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
                       enum dsa_tag_protocol mp)
 {
-       return DSA_TAG_PROTO_QCA;
+       return DSA_TAG_PROTO_IPQ4019;
 }
 
 static const struct dsa_switch_ops qca8k_switch_ops = {
@@ -2011,7 +1281,9 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
        .port_fdb_del           = qca8k_port_fdb_del,
        .port_fdb_dump          = qca8k_port_fdb_dump,
        .port_vlan_filtering    = qca8k_port_vlan_filtering,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
        .port_vlan_prepare      = qca8k_port_vlan_prepare,
+#endif
        .port_vlan_add          = qca8k_port_vlan_add,
        .port_vlan_del          = qca8k_port_vlan_del,
        .phylink_validate       = qca8k_phylink_validate,
@@ -2019,172 +1291,127 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
        .phylink_mac_config     = qca8k_phylink_mac_config,
        .phylink_mac_link_down  = qca8k_phylink_mac_link_down,
        .phylink_mac_link_up    = qca8k_phylink_mac_link_up,
-       .get_phy_flags          = qca8k_get_phy_flags,
 };
 
-static int qca8k_read_switch_id(struct qca8k_priv *priv)
-{
-       const struct qca8k_match_data *data;
-       u32 val;
-       u8 id;
-       int ret;
-
-       /* get the switches ID from the compatible */
-       data = of_device_get_match_data(priv->dev);
-       if (!data)
-               return -ENODEV;
-
-       ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
-       if (ret < 0)
-               return -ENODEV;
-
-       id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK);
-       if (id != data->id) {
-               dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
-               return -ENODEV;
-       }
-
-       priv->switch_id = id;
-
-       /* Save revision to communicate to the internal PHY driver */
-       priv->switch_revision = (val & QCA8K_MASK_CTRL_REV_ID_MASK);
-
-       return 0;
-}
-
 static int
-qca8k_sw_probe(struct mdio_device *mdiodev)
+qca8k_ipq4019_probe(struct platform_device *pdev)
 {
        struct qca8k_priv *priv;
+       void __iomem *base, *psgmii;
+       struct device_node *np = pdev->dev.of_node, *mdio_np, *psgmii_ethphy_np;
        int ret;
 
-       /* allocate the private data struct so that we can probe the switches
-        * ID register
-        */
-       priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
-       priv->bus = mdiodev->bus;
-       priv->dev = &mdiodev->dev;
+       priv->dev = &pdev->dev;
 
-       priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset",
-                                                  GPIOD_ASIS);
-       if (IS_ERR(priv->reset_gpio))
-               return PTR_ERR(priv->reset_gpio);
+       base = devm_platform_ioremap_resource_byname(pdev, "base");
+       if (IS_ERR(base))
+               return PTR_ERR(base);
 
-       if (priv->reset_gpio) {
-               gpiod_set_value_cansleep(priv->reset_gpio, 1);
-               /* The active low duration must be greater than 10 ms
-                * and checkpatch.pl wants 20 ms.
-                */
-               msleep(20);
-               gpiod_set_value_cansleep(priv->reset_gpio, 0);
+       priv->regmap = devm_regmap_init_mmio(priv->dev, base,
+                                            &qca8k_ipq4019_regmap_config);
+       if (IS_ERR(priv->regmap)) {
+               ret = PTR_ERR(priv->regmap);
+               dev_err(priv->dev, "base regmap initialization failed, %d\n", ret);
+               return ret;
        }
 
-       /* Check the detected switch id */
-       ret = qca8k_read_switch_id(priv);
-       if (ret)
+       psgmii = devm_platform_ioremap_resource_byname(pdev, "psgmii_phy");
+       if (IS_ERR(psgmii))
+               return PTR_ERR(psgmii);
+
+       priv->psgmii = devm_regmap_init_mmio(priv->dev, psgmii,
+                                            &qca8k_ipq4019_psgmii_phy_regmap_config);
+       if (IS_ERR(priv->psgmii)) {
+               ret = PTR_ERR(priv->psgmii);
+               dev_err(priv->dev, "PSGMII regmap initialization failed, %d\n", ret);
                return ret;
+       }
+
+       mdio_np = of_parse_phandle(np, "mdio", 0);
+       if (!mdio_np) {
+               dev_err(&pdev->dev, "unable to get MDIO bus phandle\n");
+               of_node_put(mdio_np);
+               return -EINVAL;
+       }
+
+       priv->bus = of_mdio_find_bus(mdio_np);
+       of_node_put(mdio_np);
+       if (!priv->bus) {
+               dev_err(&pdev->dev, "unable to find MDIO bus\n");
+               return -EPROBE_DEFER;
+       }
 
-       priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+       psgmii_ethphy_np = of_parse_phandle(np, "psgmii-ethphy", 0);
+       if (!psgmii_ethphy_np) {
+               dev_dbg(&pdev->dev, "unable to get PSGMII eth PHY phandle\n");
+               of_node_put(psgmii_ethphy_np);
+       }
+
+       if (psgmii_ethphy_np) {
+               priv->psgmii_ethphy = of_phy_find_device(psgmii_ethphy_np);
+               of_node_put(psgmii_ethphy_np);
+               if (!priv->psgmii_ethphy) {
+                       dev_err(&pdev->dev, "unable to get PSGMII eth PHY\n");
+                       return -ENODEV;
+               }
+       }
+
+       priv->ds = devm_kzalloc(priv->dev, sizeof(*priv->ds), GFP_KERNEL);
        if (!priv->ds)
                return -ENOMEM;
 
-       priv->ds->dev = &mdiodev->dev;
+       priv->ds->dev = priv->dev;
        priv->ds->num_ports = QCA8K_NUM_PORTS;
-       priv->ds->configure_vlan_while_not_filtering = true;
        priv->ds->priv = priv;
        priv->ops = qca8k_switch_ops;
        priv->ds->ops = &priv->ops;
+
        mutex_init(&priv->reg_mutex);
-       dev_set_drvdata(&mdiodev->dev, priv);
+       platform_set_drvdata(pdev, priv);
 
        return dsa_register_switch(priv->ds);
 }
 
-static void
-qca8k_sw_remove(struct mdio_device *mdiodev)
+static int
+qca8k_ipq4019_remove(struct platform_device *pdev)
 {
-       struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
+       struct qca8k_priv *priv = dev_get_drvdata(&pdev->dev);
        int i;
 
+       if (!priv)
+               return 0;
+
        for (i = 0; i < QCA8K_NUM_PORTS; i++)
                qca8k_port_set_status(priv, i, 0);
 
        dsa_unregister_switch(priv->ds);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static void
-qca8k_set_pm(struct qca8k_priv *priv, int enable)
-{
-       int i;
-
-       for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-               if (!priv->port_sts[i].enabled)
-                       continue;
-
-               qca8k_port_set_status(priv, i, enable);
-       }
-}
 
-static int qca8k_suspend(struct device *dev)
-{
-       struct qca8k_priv *priv = dev_get_drvdata(dev);
-
-       qca8k_set_pm(priv, 0);
-
-       return dsa_switch_suspend(priv->ds);
-}
+       dev_set_drvdata(&pdev->dev, NULL);
 
-static int qca8k_resume(struct device *dev)
-{
-       struct qca8k_priv *priv = dev_get_drvdata(dev);
-
-       qca8k_set_pm(priv, 1);
-
-       return dsa_switch_resume(priv->ds);
+       return 0;
 }
-#endif /* CONFIG_PM_SLEEP */
-
-static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
-                        qca8k_suspend, qca8k_resume);
-
-static const struct qca8k_match_data qca8327 = {
-       .id = QCA8K_ID_QCA8327,
-       .reduced_package = true,
-};
-
-static const struct qca8k_match_data qca8328 = {
-       .id = QCA8K_ID_QCA8327,
-};
-
-static const struct qca8k_match_data qca833x = {
-       .id = QCA8K_ID_QCA8337,
-};
 
-static const struct of_device_id qca8k_of_match[] = {
-       { .compatible = "qca,qca8327", .data = &qca8327 },
-       { .compatible = "qca,qca8328", .data = &qca8328 },
-       { .compatible = "qca,qca8334", .data = &qca833x },
-       { .compatible = "qca,qca8337", .data = &qca833x },
+static const struct of_device_id qca8k_ipq4019_of_match[] = {
+       { .compatible = "qca,ipq4019-qca8337n" },
        { /* sentinel */ },
 };
 
-static struct mdio_driver qca8kmdio_driver = {
-       .probe  = qca8k_sw_probe,
-       .remove = qca8k_sw_remove,
-       .mdiodrv.driver = {
-               .name = "qca8k",
-               .of_match_table = qca8k_of_match,
-               .pm = &qca8k_pm_ops,
+static struct platform_driver qca8k_ipq4019_driver = {
+       .probe = qca8k_ipq4019_probe,
+       .remove = qca8k_ipq4019_remove,
+       .driver = {
+               .name = "qca8k-ipq4019",
+               .of_match_table = qca8k_ipq4019_of_match,
        },
 };
 
-mdio_module_driver(qca8kmdio_driver);
+module_platform_driver(qca8k_ipq4019_driver);
 
 MODULE_AUTHOR("Mathieu Olivari, John Crispin <john@phrozen.org>");
-MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family");
+MODULE_AUTHOR("Gabor Juhos <j4g8y7@gmail.com>, Robert Marko <robert.marko@sartura.hr>");
+MODULE_DESCRIPTION("Qualcomm IPQ4019 built-in switch driver");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:qca8k");
index e10571a398c98a1cabec9ca85ab036e9589bef16..a36eb0dadcb5e225a3afde4366d8cc119694a788 100644 (file)
@@ -8,19 +8,12 @@
 #ifndef __QCA8K_H
 #define __QCA8K_H
 
-#include <linux/delay.h>
 #include <linux/regmap.h>
-#include <linux/gpio.h>
 
-#define QCA8K_NUM_PORTS                                        7
-#define QCA8K_NUM_CPU_PORTS                            2
+#define QCA8K_NUM_PORTS                                        6
+#define QCA8K_CPU_PORT                                 0
 #define QCA8K_MAX_MTU                                  9000
 
-#define PHY_ID_QCA8327                                 0x004dd034
-#define QCA8K_ID_QCA8327                               0x12
-#define PHY_ID_QCA8337                                 0x004dd036
-#define QCA8K_ID_QCA8337                               0x13
-
 #define QCA8K_BUSY_WAIT_TIMEOUT                                2000
 
 #define QCA8K_NUM_FDB_RECORDS                          2048
 #define   QCA8K_MASK_CTRL_REV_ID(x)                    ((x) >> 0)
 #define   QCA8K_MASK_CTRL_DEVICE_ID_MASK               GENMASK(15, 8)
 #define   QCA8K_MASK_CTRL_DEVICE_ID(x)                 ((x) >> 8)
-#define QCA8K_REG_PORT0_PAD_CTRL                       0x004
-#define   QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE     BIT(19)
-#define   QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE     BIT(18)
-#define QCA8K_REG_PORT5_PAD_CTRL                       0x008
-#define QCA8K_REG_PORT6_PAD_CTRL                       0x00c
-#define   QCA8K_PORT_PAD_RGMII_EN                      BIT(26)
-#define   QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK           GENMASK(23, 22)
-#define   QCA8K_PORT_PAD_RGMII_TX_DELAY(x)             ((x) << 22)
-#define   QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK           GENMASK(21, 20)
-#define   QCA8K_PORT_PAD_RGMII_RX_DELAY(x)             ((x) << 20)
-#define          QCA8K_PORT_PAD_RGMII_TX_DELAY_EN              BIT(25)
-#define   QCA8K_PORT_PAD_RGMII_RX_DELAY_EN             BIT(24)
-#define   QCA8K_MAX_DELAY                              3
-#define   QCA8K_PORT_PAD_SGMII_EN                      BIT(7)
-#define QCA8K_REG_PWS                                  0x010
-#define   QCA8K_PWS_POWER_ON_SEL                       BIT(31)
-/* This reg is only valid for QCA832x and toggle the package
- * type from 176 pin (by default) to 148 pin used on QCA8327
+#define QCA8K_REG_RGMII_CTRL                           0x004
+#define   QCA8K_RGMII_CTRL_RGMII_RXC                   GENMASK(1, 0)
+#define   QCA8K_RGMII_CTRL_RGMII_TXC                   GENMASK(9, 8)
+/* Some kind of CLK selection
+ * 0: gcc_ess_dly2ns
+ * 1: gcc_ess_clk
  */
-#define   QCA8327_PWS_PACKAGE148_EN                    BIT(30)
-#define   QCA8K_PWS_LED_OPEN_EN_CSR                    BIT(24)
-#define   QCA8K_PWS_SERDES_AEN_DIS                     BIT(7)
+#define   QCA8K_RGMII_CTRL_CLK                         BIT(10)
+#define   QCA8K_RGMII_CTRL_DELAY_RMII0                 GENMASK(17, 16)
+#define   QCA8K_RGMII_CTRL_INVERT_RMII0_REF_CLK                BIT(18)
+#define   QCA8K_RGMII_CTRL_DELAY_RMII1                 GENMASK(20, 19)
+#define   QCA8K_RGMII_CTRL_INVERT_RMII1_REF_CLK                BIT(21)
+#define   QCA8K_RGMII_CTRL_INVERT_RMII0_MASTER_EN      BIT(24)
+#define   QCA8K_RGMII_CTRL_INVERT_RMII1_MASTER_EN      BIT(25)
 #define QCA8K_REG_MODULE_EN                            0x030
 #define   QCA8K_MODULE_EN_MIB                          BIT(0)
 #define QCA8K_REG_MIB                                  0x034
 #define   QCA8K_MIB_FLUSH                              BIT(24)
 #define   QCA8K_MIB_CPU_KEEP                           BIT(20)
 #define   QCA8K_MIB_BUSY                               BIT(17)
-#define QCA8K_MDIO_MASTER_CTRL                         0x3c
-#define   QCA8K_MDIO_MASTER_BUSY                       BIT(31)
-#define   QCA8K_MDIO_MASTER_EN                         BIT(30)
-#define   QCA8K_MDIO_MASTER_READ                       BIT(27)
-#define   QCA8K_MDIO_MASTER_WRITE                      0
-#define   QCA8K_MDIO_MASTER_SUP_PRE                    BIT(26)
-#define   QCA8K_MDIO_MASTER_PHY_ADDR(x)                        ((x) << 21)
-#define   QCA8K_MDIO_MASTER_REG_ADDR(x)                        ((x) << 16)
-#define   QCA8K_MDIO_MASTER_DATA(x)                    (x)
-#define   QCA8K_MDIO_MASTER_DATA_MASK                  GENMASK(15, 0)
-#define   QCA8K_MDIO_MASTER_MAX_PORTS                  5
-#define   QCA8K_MDIO_MASTER_MAX_REG                    32
 #define QCA8K_GOL_MAC_ADDR0                            0x60
 #define QCA8K_GOL_MAC_ADDR1                            0x64
 #define QCA8K_MAX_FRAME_SIZE                           0x78
 #define   QCA8K_SGMII_MODE_CTRL_PHY                    (1 << 22)
 #define   QCA8K_SGMII_MODE_CTRL_MAC                    (2 << 22)
 
-/* MAC_PWR_SEL registers */
-#define QCA8K_REG_MAC_PWR_SEL                          0x0e4
-#define   QCA8K_MAC_PWR_RGMII1_1_8V                    BIT(18)
-#define   QCA8K_MAC_PWR_RGMII0_1_8V                    BIT(19)
-
 /* EEE control registers */
 #define QCA8K_REG_EEE_CTRL                             0x100
 #define  QCA8K_REG_EEE_CTRL_LPI_EN(_i)                 ((_i + 1) * 2)
 /* MIB registers */
 #define QCA8K_PORT_MIB_COUNTER(_i)                     (0x1000 + (_i) * 0x100)
 
-/* QCA specific MII registers */
-#define MII_ATH_MMD_ADDR                               0x0d
-#define MII_ATH_MMD_DATA                               0x0e
+/* IPQ4019 PSGMII PHY registers */
+#define PSGMIIPHY_MODE_CONTROL                         0x1b4
+#define   PSGMIIPHY_MODE_ATHR_CSCO_MODE_25M            BIT(0)
+#define PSGMIIPHY_TX_CONTROL                           0x288
+#define   PSGMIIPHY_TX_CONTROL_MAGIC_VALUE             0x8380
+#define PSGMIIPHY_VCO_CALIBRATION_CONTROL_REGISTER_1   0x9c
+#define   PSGMIIPHY_REG_PLL_VCO_CALIB_RESTART          BIT(14)
+#define PSGMIIPHY_VCO_CALIBRATION_CONTROL_REGISTER_2   0xa0
+#define   PSGMIIPHY_REG_PLL_VCO_CALIB_READY            BIT(0)
 
 enum {
        QCA8K_PORT_SPEED_10M = 0,
@@ -260,29 +234,7 @@ struct ar8xxx_port_status {
        int enabled;
 };
 
-struct qca8k_match_data {
-       u8 id;
-       bool reduced_package;
-};
-
-enum {
-       QCA8K_CPU_PORT0,
-       QCA8K_CPU_PORT6,
-};
-
-struct qca8k_ports_config {
-       bool sgmii_rx_clk_falling_edge;
-       bool sgmii_tx_clk_falling_edge;
-       bool sgmii_enable_pll;
-       u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
-       u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
-};
-
 struct qca8k_priv {
-       u8 switch_id;
-       u8 switch_revision;
-       bool legacy_phy_port_mapping;
-       struct qca8k_ports_config ports_config;
        struct regmap *regmap;
        struct mii_bus *bus;
        struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
@@ -290,8 +242,12 @@ struct qca8k_priv {
        struct mutex reg_mutex;
        struct device *dev;
        struct dsa_switch_ops ops;
-       struct gpio_desc *reset_gpio;
        unsigned int port_mtu[QCA8K_NUM_PORTS];
+
+       /* IPQ4019 specific */
+       struct regmap *psgmii;
+       bool psgmii_calibrated;
+       struct phy_device *psgmii_ethphy;
 };
 
 struct qca8k_mib_desc {
diff --git a/target/linux/ipq40xx/patches-5.10/705-net-dsa-add-Qualcomm-IPQ4019-built-in-switch-support.patch b/target/linux/ipq40xx/patches-5.10/705-net-dsa-add-Qualcomm-IPQ4019-built-in-switch-support.patch
new file mode 100644 (file)
index 0000000..ed201b7
--- /dev/null
@@ -0,0 +1,53 @@
+From b5f71652b85a85ea53162e9e2b760b84fd0d254f Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Mon, 1 Nov 2021 18:10:28 +0100
+Subject: [PATCH] net: dsa: add Qualcomm IPQ4019 built-in switch support
+
+Qualcomm IPQ40xx SoC-s have a variant of QCA8337N switch built-in.
+
+It shares most of the stuff with its external counterpart, however it is
+modified for the SoC.
+Namely, it doesn't have second CPU port (Port 6), so it has 6 ports
+instead of 7.
+It also has no built-in PHY-s but rather requires external PSGMII based
+companion PHY-s (QCA8072 and QCA8075) for which it first needs to carry
+out calibration before using them.
+PSGMII has a SoC built-in PHY that is used to connect to the PHY-s which
+unfortunately requires some magic values as the datasheet doesnt document
+the bits that are being set or the register at all.
+
+Since its built-in it is MMIO like other peripherals and doesn't have its
+own MDIO bus but depends on the SoC provided one.
+
+CPU connection is at Port 0 and it uses some kind of a internal connection
+and no traditional RGMII/SGMII.
+It also doesn't use in-band tagging like other qca8k switches so a shinfo
+based tagger is used.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ drivers/net/dsa/qca/Kconfig  | 9 +++++++++
+ drivers/net/dsa/qca/Makefile | 1 +
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/net/dsa/qca/Kconfig
++++ b/drivers/net/dsa/qca/Kconfig
+@@ -7,3 +7,12 @@ config NET_DSA_AR9331
+       help
+         This enables support for the Qualcomm Atheros AR9331 built-in Ethernet
+         switch.
++
++config NET_DSA_QCA8K_IPQ4019
++      tristate "Qualcomm Atheros IPQ4019 built-in Ethernet switch support"
++      depends on HAS_IOMEM && NET_DSA
++      select NET_DSA_TAG_IPQ4019
++      select REGMAP
++      help
++        This enables support for the Qualcomm Atheros IPQ4019 SoC built-in
++        Ethernet switch.
+--- a/drivers/net/dsa/qca/Makefile
++++ b/drivers/net/dsa/qca/Makefile
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ obj-$(CONFIG_NET_DSA_AR9331)  += ar9331.o
++obj-$(CONFIG_NET_DSA_QCA8K_IPQ4019)   += qca8k-ipq4019.o
diff --git a/target/linux/ipq40xx/patches-5.10/706-arm-dts-ipq4019-add-switch-node.patch b/target/linux/ipq40xx/patches-5.10/706-arm-dts-ipq4019-add-switch-node.patch
new file mode 100644 (file)
index 0000000..a231c73
--- /dev/null
@@ -0,0 +1,98 @@
+From ebb62523990a27b3a25e422fa575619f7f725a20 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Mon, 1 Nov 2021 18:15:04 +0100
+Subject: [PATCH] arm: dts: ipq4019: add switch node
+
+Since the built-in IPQ40xx switch now has a driver, add the required node
+for it to work.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 78 +++++++++++++++++++++++++++++
+ 1 file changed, 78 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -590,6 +590,82 @@
+                       status = "disabled";
+               };
++              switch: switch@c000000 {
++                      compatible = "qca,ipq4019-qca8337n";
++                      reg = <0xc000000 0x80000>, <0x98000 0x800>;
++                      reg-names = "base", "psgmii_phy";
++                      resets = <&gcc ESS_PSGMII_ARES>;
++                      reset-names = "psgmii_rst";
++                      mdio = <&mdio>;
++                      psgmii-ethphy = <&psgmiiphy>;
++
++                      status = "disabled";
++
++                      ports {
++                              #address-cells = <1>;
++                              #size-cells = <0>;
++
++                              port@0 { /* MAC0 */
++                                      reg = <0>;
++                                      label = "cpu";
++                                      ethernet = <&gmac>;
++                                      phy-mode = "internal";
++
++                                      fixed-link {
++                                              speed = <1000>;
++                                              full-duplex;
++                                              pause;
++                                              asym-pause;
++                                      };
++                              };
++
++                              swport1: port@1 { /* MAC1 */
++                                      reg = <1>;
++                                      label = "lan1";
++                                      phy-handle = <&ethphy0>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++
++                              swport2: port@2 { /* MAC2 */
++                                      reg = <2>;
++                                      label = "lan2";
++                                      phy-handle = <&ethphy1>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++
++                              swport3: port@3 { /* MAC3 */
++                                      reg = <3>;
++                                      label = "lan3";
++                                      phy-handle = <&ethphy2>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++
++                              swport4: port@4 { /* MAC4 */
++                                      reg = <4>;
++                                      label = "lan4";
++                                      phy-handle = <&ethphy3>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++
++                              swport5: port@5 { /* MAC5 */
++                                      reg = <5>;
++                                      label = "wan";
++                                      phy-handle = <&ethphy4>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++                      };
++              };
++
+               gmac: ethernet@c080000 {
+                       compatible = "qcom,ipq4019-ess-edma";
+                       reg = <0xc080000 0x8000>;
diff --git a/target/linux/ipq40xx/patches-5.10/706-dt-bindings-net-add-QCA807x-PHY.patch b/target/linux/ipq40xx/patches-5.10/706-dt-bindings-net-add-QCA807x-PHY.patch
deleted file mode 100644 (file)
index dfb8d69..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-From c66863c1ba8995b61e6d727d78a241c734f5bb57 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robert.marko@sartura.hr>
-Date: Thu, 1 Oct 2020 15:05:35 +0200
-Subject: [PATCH] dt-bindings: net: add QCA807x PHY
-
-Add DT bindings for Qualcomm QCA807x PHY series.
-
-Signed-off-by: Robert Marko <robert.marko@sartura.hr>
----
- include/dt-bindings/net/qcom-qca807x.h | 45 ++++++++++++++++++++++++++
- 1 file changed, 45 insertions(+)
- create mode 100644 include/dt-bindings/net/qcom-qca807x.h
-
---- /dev/null
-+++ b/include/dt-bindings/net/qcom-qca807x.h
-@@ -0,0 +1,45 @@
-+/* SPDX-License-Identifier: GPL-2.0-or-later */
-+/*
-+ * Device Tree constants for the Qualcomm QCA807X PHYs
-+ */
-+
-+#ifndef _DT_BINDINGS_QCOM_QCA807X_H
-+#define _DT_BINDINGS_QCOM_QCA807X_H
-+
-+#define PSGMII_QSGMII_TX_DRIVER_140MV 0
-+#define PSGMII_QSGMII_TX_DRIVER_160MV 1
-+#define PSGMII_QSGMII_TX_DRIVER_180MV 2
-+#define PSGMII_QSGMII_TX_DRIVER_200MV 3
-+#define PSGMII_QSGMII_TX_DRIVER_220MV 4
-+#define PSGMII_QSGMII_TX_DRIVER_240MV 5
-+#define PSGMII_QSGMII_TX_DRIVER_260MV 6
-+#define PSGMII_QSGMII_TX_DRIVER_280MV 7
-+#define PSGMII_QSGMII_TX_DRIVER_300MV 8
-+#define PSGMII_QSGMII_TX_DRIVER_320MV 9
-+#define PSGMII_QSGMII_TX_DRIVER_400MV 10
-+#define PSGMII_QSGMII_TX_DRIVER_500MV 11
-+/* Default value */
-+#define PSGMII_QSGMII_TX_DRIVER_600MV 12
-+
-+/* Full amplitude, full bias current */
-+#define QCA807X_CONTROL_DAC_FULL_VOLT_BIAS            0
-+/* Amplitude follow DSP (amplitude is adjusted based on cable length), half bias current */
-+#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS                1
-+/* Full amplitude, bias current follow DSP (bias current is adjusted based on cable length) */
-+#define QCA807X_CONTROL_DAC_FULL_VOLT_DSP_BIAS                2
-+/* Both amplitude and bias current follow DSP */
-+#define QCA807X_CONTROL_DAC_DSP_VOLT_BIAS             3
-+/* Full amplitude, half bias current */
-+#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS               4
-+/* Amplitude follow DSP setting; 1/4 bias current when cable<10m,
-+ * otherwise half bias current
-+ */
-+#define QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS     5
-+/* Full amplitude; same bias current setting with “010” and “011”,
-+ * but half more bias is reduced when cable <10m
-+ */
-+#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS_SHORT 6
-+/* Amplitude follow DSP; same bias current setting with “110”, default value */
-+#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS_SHORT  7
-+
-+#endif
diff --git a/target/linux/ipq40xx/patches-5.10/707-dt-bindings-net-add-QCA807x-PHY.patch b/target/linux/ipq40xx/patches-5.10/707-dt-bindings-net-add-QCA807x-PHY.patch
new file mode 100644 (file)
index 0000000..dfb8d69
--- /dev/null
@@ -0,0 +1,61 @@
+From c66863c1ba8995b61e6d727d78a241c734f5bb57 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Thu, 1 Oct 2020 15:05:35 +0200
+Subject: [PATCH] dt-bindings: net: add QCA807x PHY
+
+Add DT bindings for Qualcomm QCA807x PHY series.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ include/dt-bindings/net/qcom-qca807x.h | 45 ++++++++++++++++++++++++++
+ 1 file changed, 45 insertions(+)
+ create mode 100644 include/dt-bindings/net/qcom-qca807x.h
+
+--- /dev/null
++++ b/include/dt-bindings/net/qcom-qca807x.h
+@@ -0,0 +1,45 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Device Tree constants for the Qualcomm QCA807X PHYs
++ */
++
++#ifndef _DT_BINDINGS_QCOM_QCA807X_H
++#define _DT_BINDINGS_QCOM_QCA807X_H
++
++#define PSGMII_QSGMII_TX_DRIVER_140MV 0
++#define PSGMII_QSGMII_TX_DRIVER_160MV 1
++#define PSGMII_QSGMII_TX_DRIVER_180MV 2
++#define PSGMII_QSGMII_TX_DRIVER_200MV 3
++#define PSGMII_QSGMII_TX_DRIVER_220MV 4
++#define PSGMII_QSGMII_TX_DRIVER_240MV 5
++#define PSGMII_QSGMII_TX_DRIVER_260MV 6
++#define PSGMII_QSGMII_TX_DRIVER_280MV 7
++#define PSGMII_QSGMII_TX_DRIVER_300MV 8
++#define PSGMII_QSGMII_TX_DRIVER_320MV 9
++#define PSGMII_QSGMII_TX_DRIVER_400MV 10
++#define PSGMII_QSGMII_TX_DRIVER_500MV 11
++/* Default value */
++#define PSGMII_QSGMII_TX_DRIVER_600MV 12
++
++/* Full amplitude, full bias current */
++#define QCA807X_CONTROL_DAC_FULL_VOLT_BIAS            0
++/* Amplitude follow DSP (amplitude is adjusted based on cable length), half bias current */
++#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS                1
++/* Full amplitude, bias current follow DSP (bias current is adjusted based on cable length) */
++#define QCA807X_CONTROL_DAC_FULL_VOLT_DSP_BIAS                2
++/* Both amplitude and bias current follow DSP */
++#define QCA807X_CONTROL_DAC_DSP_VOLT_BIAS             3
++/* Full amplitude, half bias current */
++#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS               4
++/* Amplitude follow DSP setting; 1/4 bias current when cable<10m,
++ * otherwise half bias current
++ */
++#define QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS     5
++/* Full amplitude; same bias current setting with “010” and “011”,
++ * but half more bias is reduced when cable <10m
++ */
++#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS_SHORT 6
++/* Amplitude follow DSP; same bias current setting with “110”, default value */
++#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS_SHORT  7
++
++#endif
diff --git a/target/linux/ipq40xx/patches-5.10/707-net-phy-Add-Qualcom-QCA807x-driver.patch b/target/linux/ipq40xx/patches-5.10/707-net-phy-Add-Qualcom-QCA807x-driver.patch
deleted file mode 100644 (file)
index 12b21a8..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-From f825cdc8bfde7616a14e2163f16303a8973031d2 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robert.marko@sartura.hr>
-Date: Wed, 7 Oct 2020 17:38:48 +0200
-Subject: [PATCH] net: phy: Add Qualcom QCA807x driver
-
-This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s.
-
-They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te, 100BASE-TX and 1000BASE-T PHY-s.
-
-They feature 2 SerDes, one for PSGMII or QSGMII connection with MAC, while second one is SGMII for connection to MAC or fiber.
-
-Both models have a combo port that supports 1000BASE-X and 100BASE-FX fiber.
-
-Each PHY inside of QCA807x series has 4 digitally controlled output only pins that natively drive LED-s.
-But some vendors used these to driver generic LED-s controlled by userspace,
-so lets enable registering each PHY as GPIO controller and add driver for it.
-
-These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x boards.
-
-Signed-off-by: Robert Marko <robert.marko@sartura.hr>
----
- drivers/net/phy/Kconfig  | 6 ++++++
- drivers/net/phy/Makefile | 1 +
- 2 files changed, 7 insertions(+)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -320,6 +320,12 @@ config AT803X_PHY
-         Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
-         QCA8337(Internal qca8k PHY) model
-+config QCA807X_PHY
-+      tristate "Qualcomm QCA807X PHYs"
-+      depends on OF_MDIO
-+      help
-+        Currently supports the QCA8072 and QCA8075 models.
-+
- config QSEMI_PHY
-       tristate "Quality Semiconductor PHYs"
-       help
---- a/drivers/net/phy/Makefile
-+++ b/drivers/net/phy/Makefile
-@@ -88,6 +88,7 @@ obj-$(CONFIG_MICROSEMI_PHY)  += mscc/
- obj-$(CONFIG_NATIONAL_PHY)    += national.o
- obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
- obj-$(CONFIG_QSEMI_PHY)               += qsemi.o
-+obj-$(CONFIG_QCA807X_PHY)             += qca807x.o
- obj-$(CONFIG_REALTEK_PHY)     += realtek.o
- obj-$(CONFIG_RENESAS_PHY)     += uPD60620.o
- obj-$(CONFIG_ROCKCHIP_PHY)    += rockchip.o
diff --git a/target/linux/ipq40xx/patches-5.10/708-arm-dts-ipq4019-QCA807x-properties.patch b/target/linux/ipq40xx/patches-5.10/708-arm-dts-ipq4019-QCA807x-properties.patch
deleted file mode 100644 (file)
index 33310d9..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-From e0fa88eaa3c176b71e563da68949ac2ab45aaa61 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robert.marko@sartura.hr>
-Date: Fri, 2 Oct 2020 10:43:26 +0200
-Subject: [PATCH] arm: dts: ipq4019: QCA807x properties
-
-This adds necessary DT properties for QCA807x PHY-s to IPQ4019 DTSI.
-
-Signed-off-by: Robert Marko <robert.marko@sartura.hr>
----
- arch/arm/boot/dts/qcom-ipq4019.dtsi | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
-+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
-@@ -8,6 +8,7 @@
- #include <dt-bindings/clock/qcom,gcc-ipq4019.h>
- #include <dt-bindings/interrupt-controller/arm-gic.h>
- #include <dt-bindings/interrupt-controller/irq.h>
-+#include <dt-bindings/net/qcom-qca807x.h>
- / {
-       #address-cells = <1>;
-@@ -645,22 +646,39 @@
-                       ethphy0: ethernet-phy@0 {
-                               reg = <0>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-                       };
-                       ethphy1: ethernet-phy@1 {
-                               reg = <1>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-                       };
-                       ethphy2: ethernet-phy@2 {
-                               reg = <2>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-                       };
-                       ethphy3: ethernet-phy@3 {
-                               reg = <3>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-                       };
-                       ethphy4: ethernet-phy@4 {
-                               reg = <4>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-+                      };
-+
-+                      psgmiiphy: psgmii-phy@5 {
-+                              reg = <5>;
-+
-+                              qcom,tx-driver-strength = <PSGMII_QSGMII_TX_DRIVER_300MV>;
-                       };
-               };
diff --git a/target/linux/ipq40xx/patches-5.10/708-net-phy-Add-Qualcom-QCA807x-driver.patch b/target/linux/ipq40xx/patches-5.10/708-net-phy-Add-Qualcom-QCA807x-driver.patch
new file mode 100644 (file)
index 0000000..12b21a8
--- /dev/null
@@ -0,0 +1,50 @@
+From f825cdc8bfde7616a14e2163f16303a8973031d2 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Wed, 7 Oct 2020 17:38:48 +0200
+Subject: [PATCH] net: phy: Add Qualcom QCA807x driver
+
+This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s.
+
+They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te, 100BASE-TX and 1000BASE-T PHY-s.
+
+They feature 2 SerDes, one for PSGMII or QSGMII connection with MAC, while second one is SGMII for connection to MAC or fiber.
+
+Both models have a combo port that supports 1000BASE-X and 100BASE-FX fiber.
+
+Each PHY inside of QCA807x series has 4 digitally controlled output only pins that natively drive LED-s.
+But some vendors used these to driver generic LED-s controlled by userspace,
+so lets enable registering each PHY as GPIO controller and add driver for it.
+
+These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x boards.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ drivers/net/phy/Kconfig  | 6 ++++++
+ drivers/net/phy/Makefile | 1 +
+ 2 files changed, 7 insertions(+)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -320,6 +320,12 @@ config AT803X_PHY
+         Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
+         QCA8337(Internal qca8k PHY) model
++config QCA807X_PHY
++      tristate "Qualcomm QCA807X PHYs"
++      depends on OF_MDIO
++      help
++        Currently supports the QCA8072 and QCA8075 models.
++
+ config QSEMI_PHY
+       tristate "Quality Semiconductor PHYs"
+       help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -88,6 +88,7 @@ obj-$(CONFIG_MICROSEMI_PHY)  += mscc/
+ obj-$(CONFIG_NATIONAL_PHY)    += national.o
+ obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
+ obj-$(CONFIG_QSEMI_PHY)               += qsemi.o
++obj-$(CONFIG_QCA807X_PHY)             += qca807x.o
+ obj-$(CONFIG_REALTEK_PHY)     += realtek.o
+ obj-$(CONFIG_RENESAS_PHY)     += uPD60620.o
+ obj-$(CONFIG_ROCKCHIP_PHY)    += rockchip.o
diff --git a/target/linux/ipq40xx/patches-5.10/709-arm-dts-ipq4019-QCA807x-properties.patch b/target/linux/ipq40xx/patches-5.10/709-arm-dts-ipq4019-QCA807x-properties.patch
new file mode 100644 (file)
index 0000000..cc4b44b
--- /dev/null
@@ -0,0 +1,61 @@
+From e0fa88eaa3c176b71e563da68949ac2ab45aaa61 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Fri, 2 Oct 2020 10:43:26 +0200
+Subject: [PATCH] arm: dts: ipq4019: QCA807x properties
+
+This adds necessary DT properties for QCA807x PHY-s to IPQ4019 DTSI.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -8,6 +8,7 @@
+ #include <dt-bindings/clock/qcom,gcc-ipq4019.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/net/qcom-qca807x.h>
+ / {
+       #address-cells = <1>;
+@@ -726,22 +727,38 @@
+                       ethphy0: ethernet-phy@0 {
+                               reg = <0>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
+                       };
+                       ethphy1: ethernet-phy@1 {
+                               reg = <1>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
+                       };
+                       ethphy2: ethernet-phy@2 {
+                               reg = <2>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
+                       };
+                       ethphy3: ethernet-phy@3 {
+                               reg = <3>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
+                       };
+                       ethphy4: ethernet-phy@4 {
+                               reg = <4>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
++                      };
++
++                      psgmiiphy: psgmii-phy@5 {
++                              reg = <5>;
++
++                              qcom,tx-driver-strength = <PSGMII_QSGMII_TX_DRIVER_300MV>;
+                       };
+               };
diff --git a/target/linux/ipq40xx/patches-5.15/705-net-dsa-add-Qualcomm-IPQ4019-built-in-switch-support.patch b/target/linux/ipq40xx/patches-5.15/705-net-dsa-add-Qualcomm-IPQ4019-built-in-switch-support.patch
new file mode 100644 (file)
index 0000000..d75b5e5
--- /dev/null
@@ -0,0 +1,56 @@
+From b5f71652b85a85ea53162e9e2b760b84fd0d254f Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Mon, 1 Nov 2021 18:10:28 +0100
+Subject: [PATCH] net: dsa: add Qualcomm IPQ4019 built-in switch support
+
+Qualcomm IPQ40xx SoC-s have a variant of QCA8337N switch built-in.
+
+It shares most of the stuff with its external counterpart, however it is
+modified for the SoC.
+Namely, it doesn't have second CPU port (Port 6), so it has 6 ports
+instead of 7.
+It also has no built-in PHY-s but rather requires external PSGMII based
+companion PHY-s (QCA8072 and QCA8075) for which it first needs to carry
+out calibration before using them.
+PSGMII has a SoC built-in PHY that is used to connect to the PHY-s which
+unfortunately requires some magic values as the datasheet doesnt document
+the bits that are being set or the register at all.
+
+Since its built-in it is MMIO like other peripherals and doesn't have its
+own MDIO bus but depends on the SoC provided one.
+
+CPU connection is at Port 0 and it uses some kind of a internal connection
+and no traditional RGMII/SGMII.
+It also doesn't use in-band tagging like other qca8k switches so a shinfo
+based tagger is used.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ drivers/net/dsa/qca/Kconfig  | 9 +++++++++
+ drivers/net/dsa/qca/Makefile | 1 +
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/net/dsa/qca/Kconfig
++++ b/drivers/net/dsa/qca/Kconfig
+@@ -15,3 +15,13 @@ config NET_DSA_QCA8K
+       help
+         This enables support for the Qualcomm Atheros QCA8K Ethernet
+         switch chips.
++
++config NET_DSA_QCA8K_IPQ4019
++      tristate "Qualcomm Atheros IPQ4019 built-in Ethernet switch support"
++      depends on HAS_IOMEM && NET_DSA
++      select NET_DSA_TAG_IPQ4019
++      select REGMAP
++      help
++        This enables support for the Qualcomm Atheros IPQ4019 SoC built-in
++        Ethernet switch.
++
+--- a/drivers/net/dsa/qca/Makefile
++++ b/drivers/net/dsa/qca/Makefile
+@@ -1,4 +1,5 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ obj-$(CONFIG_NET_DSA_AR9331)  += ar9331.o
++obj-$(CONFIG_NET_DSA_QCA8K_IPQ4019)   += qca8k-ipq4019.o
+ obj-$(CONFIG_NET_DSA_QCA8K)   += qca8k.o
+ qca8k-y                       += qca8k-common.o qca8k-8xxx.o
diff --git a/target/linux/ipq40xx/patches-5.15/706-arm-dts-ipq4019-add-switch-node.patch b/target/linux/ipq40xx/patches-5.15/706-arm-dts-ipq4019-add-switch-node.patch
new file mode 100644 (file)
index 0000000..a231c73
--- /dev/null
@@ -0,0 +1,98 @@
+From ebb62523990a27b3a25e422fa575619f7f725a20 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Mon, 1 Nov 2021 18:15:04 +0100
+Subject: [PATCH] arm: dts: ipq4019: add switch node
+
+Since the built-in IPQ40xx switch now has a driver, add the required node
+for it to work.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 78 +++++++++++++++++++++++++++++
+ 1 file changed, 78 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -590,6 +590,82 @@
+                       status = "disabled";
+               };
++              switch: switch@c000000 {
++                      compatible = "qca,ipq4019-qca8337n";
++                      reg = <0xc000000 0x80000>, <0x98000 0x800>;
++                      reg-names = "base", "psgmii_phy";
++                      resets = <&gcc ESS_PSGMII_ARES>;
++                      reset-names = "psgmii_rst";
++                      mdio = <&mdio>;
++                      psgmii-ethphy = <&psgmiiphy>;
++
++                      status = "disabled";
++
++                      ports {
++                              #address-cells = <1>;
++                              #size-cells = <0>;
++
++                              port@0 { /* MAC0 */
++                                      reg = <0>;
++                                      label = "cpu";
++                                      ethernet = <&gmac>;
++                                      phy-mode = "internal";
++
++                                      fixed-link {
++                                              speed = <1000>;
++                                              full-duplex;
++                                              pause;
++                                              asym-pause;
++                                      };
++                              };
++
++                              swport1: port@1 { /* MAC1 */
++                                      reg = <1>;
++                                      label = "lan1";
++                                      phy-handle = <&ethphy0>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++
++                              swport2: port@2 { /* MAC2 */
++                                      reg = <2>;
++                                      label = "lan2";
++                                      phy-handle = <&ethphy1>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++
++                              swport3: port@3 { /* MAC3 */
++                                      reg = <3>;
++                                      label = "lan3";
++                                      phy-handle = <&ethphy2>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++
++                              swport4: port@4 { /* MAC4 */
++                                      reg = <4>;
++                                      label = "lan4";
++                                      phy-handle = <&ethphy3>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++
++                              swport5: port@5 { /* MAC5 */
++                                      reg = <5>;
++                                      label = "wan";
++                                      phy-handle = <&ethphy4>;
++                                      phy-mode = "psgmii";
++
++                                      status = "disabled";
++                              };
++                      };
++              };
++
+               gmac: ethernet@c080000 {
+                       compatible = "qcom,ipq4019-ess-edma";
+                       reg = <0xc080000 0x8000>;
diff --git a/target/linux/ipq40xx/patches-5.15/706-dt-bindings-net-add-QCA807x-PHY.patch b/target/linux/ipq40xx/patches-5.15/706-dt-bindings-net-add-QCA807x-PHY.patch
deleted file mode 100644 (file)
index dfb8d69..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-From c66863c1ba8995b61e6d727d78a241c734f5bb57 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robert.marko@sartura.hr>
-Date: Thu, 1 Oct 2020 15:05:35 +0200
-Subject: [PATCH] dt-bindings: net: add QCA807x PHY
-
-Add DT bindings for Qualcomm QCA807x PHY series.
-
-Signed-off-by: Robert Marko <robert.marko@sartura.hr>
----
- include/dt-bindings/net/qcom-qca807x.h | 45 ++++++++++++++++++++++++++
- 1 file changed, 45 insertions(+)
- create mode 100644 include/dt-bindings/net/qcom-qca807x.h
-
---- /dev/null
-+++ b/include/dt-bindings/net/qcom-qca807x.h
-@@ -0,0 +1,45 @@
-+/* SPDX-License-Identifier: GPL-2.0-or-later */
-+/*
-+ * Device Tree constants for the Qualcomm QCA807X PHYs
-+ */
-+
-+#ifndef _DT_BINDINGS_QCOM_QCA807X_H
-+#define _DT_BINDINGS_QCOM_QCA807X_H
-+
-+#define PSGMII_QSGMII_TX_DRIVER_140MV 0
-+#define PSGMII_QSGMII_TX_DRIVER_160MV 1
-+#define PSGMII_QSGMII_TX_DRIVER_180MV 2
-+#define PSGMII_QSGMII_TX_DRIVER_200MV 3
-+#define PSGMII_QSGMII_TX_DRIVER_220MV 4
-+#define PSGMII_QSGMII_TX_DRIVER_240MV 5
-+#define PSGMII_QSGMII_TX_DRIVER_260MV 6
-+#define PSGMII_QSGMII_TX_DRIVER_280MV 7
-+#define PSGMII_QSGMII_TX_DRIVER_300MV 8
-+#define PSGMII_QSGMII_TX_DRIVER_320MV 9
-+#define PSGMII_QSGMII_TX_DRIVER_400MV 10
-+#define PSGMII_QSGMII_TX_DRIVER_500MV 11
-+/* Default value */
-+#define PSGMII_QSGMII_TX_DRIVER_600MV 12
-+
-+/* Full amplitude, full bias current */
-+#define QCA807X_CONTROL_DAC_FULL_VOLT_BIAS            0
-+/* Amplitude follow DSP (amplitude is adjusted based on cable length), half bias current */
-+#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS                1
-+/* Full amplitude, bias current follow DSP (bias current is adjusted based on cable length) */
-+#define QCA807X_CONTROL_DAC_FULL_VOLT_DSP_BIAS                2
-+/* Both amplitude and bias current follow DSP */
-+#define QCA807X_CONTROL_DAC_DSP_VOLT_BIAS             3
-+/* Full amplitude, half bias current */
-+#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS               4
-+/* Amplitude follow DSP setting; 1/4 bias current when cable<10m,
-+ * otherwise half bias current
-+ */
-+#define QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS     5
-+/* Full amplitude; same bias current setting with “010” and “011”,
-+ * but half more bias is reduced when cable <10m
-+ */
-+#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS_SHORT 6
-+/* Amplitude follow DSP; same bias current setting with “110”, default value */
-+#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS_SHORT  7
-+
-+#endif
diff --git a/target/linux/ipq40xx/patches-5.15/707-dt-bindings-net-add-QCA807x-PHY.patch b/target/linux/ipq40xx/patches-5.15/707-dt-bindings-net-add-QCA807x-PHY.patch
new file mode 100644 (file)
index 0000000..dfb8d69
--- /dev/null
@@ -0,0 +1,61 @@
+From c66863c1ba8995b61e6d727d78a241c734f5bb57 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Thu, 1 Oct 2020 15:05:35 +0200
+Subject: [PATCH] dt-bindings: net: add QCA807x PHY
+
+Add DT bindings for Qualcomm QCA807x PHY series.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ include/dt-bindings/net/qcom-qca807x.h | 45 ++++++++++++++++++++++++++
+ 1 file changed, 45 insertions(+)
+ create mode 100644 include/dt-bindings/net/qcom-qca807x.h
+
+--- /dev/null
++++ b/include/dt-bindings/net/qcom-qca807x.h
+@@ -0,0 +1,45 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Device Tree constants for the Qualcomm QCA807X PHYs
++ */
++
++#ifndef _DT_BINDINGS_QCOM_QCA807X_H
++#define _DT_BINDINGS_QCOM_QCA807X_H
++
++#define PSGMII_QSGMII_TX_DRIVER_140MV 0
++#define PSGMII_QSGMII_TX_DRIVER_160MV 1
++#define PSGMII_QSGMII_TX_DRIVER_180MV 2
++#define PSGMII_QSGMII_TX_DRIVER_200MV 3
++#define PSGMII_QSGMII_TX_DRIVER_220MV 4
++#define PSGMII_QSGMII_TX_DRIVER_240MV 5
++#define PSGMII_QSGMII_TX_DRIVER_260MV 6
++#define PSGMII_QSGMII_TX_DRIVER_280MV 7
++#define PSGMII_QSGMII_TX_DRIVER_300MV 8
++#define PSGMII_QSGMII_TX_DRIVER_320MV 9
++#define PSGMII_QSGMII_TX_DRIVER_400MV 10
++#define PSGMII_QSGMII_TX_DRIVER_500MV 11
++/* Default value */
++#define PSGMII_QSGMII_TX_DRIVER_600MV 12
++
++/* Full amplitude, full bias current */
++#define QCA807X_CONTROL_DAC_FULL_VOLT_BIAS            0
++/* Amplitude follow DSP (amplitude is adjusted based on cable length), half bias current */
++#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS                1
++/* Full amplitude, bias current follow DSP (bias current is adjusted based on cable length) */
++#define QCA807X_CONTROL_DAC_FULL_VOLT_DSP_BIAS                2
++/* Both amplitude and bias current follow DSP */
++#define QCA807X_CONTROL_DAC_DSP_VOLT_BIAS             3
++/* Full amplitude, half bias current */
++#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS               4
++/* Amplitude follow DSP setting; 1/4 bias current when cable<10m,
++ * otherwise half bias current
++ */
++#define QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS     5
++/* Full amplitude; same bias current setting with “010” and “011”,
++ * but half more bias is reduced when cable <10m
++ */
++#define QCA807X_CONTROL_DAC_FULL_VOLT_HALF_BIAS_SHORT 6
++/* Amplitude follow DSP; same bias current setting with “110”, default value */
++#define QCA807X_CONTROL_DAC_DSP_VOLT_HALF_BIAS_SHORT  7
++
++#endif
diff --git a/target/linux/ipq40xx/patches-5.15/707-net-phy-Add-Qualcom-QCA807x-driver.patch b/target/linux/ipq40xx/patches-5.15/707-net-phy-Add-Qualcom-QCA807x-driver.patch
deleted file mode 100644 (file)
index 87be598..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-From f825cdc8bfde7616a14e2163f16303a8973031d2 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robert.marko@sartura.hr>
-Date: Wed, 7 Oct 2020 17:38:48 +0200
-Subject: [PATCH] net: phy: Add Qualcom QCA807x driver
-
-This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s.
-
-They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te, 100BASE-TX and 1000BASE-T PHY-s.
-
-They feature 2 SerDes, one for PSGMII or QSGMII connection with MAC, while second one is SGMII for connection to MAC or fiber.
-
-Both models have a combo port that supports 1000BASE-X and 100BASE-FX fiber.
-
-Each PHY inside of QCA807x series has 4 digitally controlled output only pins that natively drive LED-s.
-But some vendors used these to driver generic LED-s controlled by userspace,
-so lets enable registering each PHY as GPIO controller and add driver for it.
-
-These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x boards.
-
-Signed-off-by: Robert Marko <robert.marko@sartura.hr>
----
- drivers/net/phy/Kconfig  | 6 ++++++
- drivers/net/phy/Makefile | 1 +
- 2 files changed, 7 insertions(+)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -346,6 +346,12 @@ config AT803X_PHY
-         Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
-         QCA8337(Internal qca8k PHY) model
-+config QCA807X_PHY
-+      tristate "Qualcomm QCA807X PHYs"
-+      depends on OF_MDIO
-+      help
-+        Currently supports the QCA8072 and QCA8075 models.
-+
- config QSEMI_PHY
-       tristate "Quality Semiconductor PHYs"
-       help
---- a/drivers/net/phy/Makefile
-+++ b/drivers/net/phy/Makefile
-@@ -92,6 +92,7 @@ obj-$(CONFIG_NATIONAL_PHY)   += national.o
- obj-$(CONFIG_NXP_C45_TJA11XX_PHY)     += nxp-c45-tja11xx.o
- obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
- obj-$(CONFIG_QSEMI_PHY)               += qsemi.o
-+obj-$(CONFIG_QCA807X_PHY)             += qca807x.o
- obj-$(CONFIG_REALTEK_PHY)     += realtek.o
- obj-$(CONFIG_RENESAS_PHY)     += uPD60620.o
- obj-$(CONFIG_ROCKCHIP_PHY)    += rockchip.o
diff --git a/target/linux/ipq40xx/patches-5.15/708-arm-dts-ipq4019-QCA807x-properties.patch b/target/linux/ipq40xx/patches-5.15/708-arm-dts-ipq4019-QCA807x-properties.patch
deleted file mode 100644 (file)
index d978693..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-From e0fa88eaa3c176b71e563da68949ac2ab45aaa61 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robert.marko@sartura.hr>
-Date: Fri, 2 Oct 2020 10:43:26 +0200
-Subject: [PATCH] arm: dts: ipq4019: QCA807x properties
-
-This adds necessary DT properties for QCA807x PHY-s to IPQ4019 DTSI.
-
-Signed-off-by: Robert Marko <robert.marko@sartura.hr>
----
- arch/arm/boot/dts/qcom-ipq4019.dtsi | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
-+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
-@@ -8,6 +8,7 @@
- #include <dt-bindings/clock/qcom,gcc-ipq4019.h>
- #include <dt-bindings/interrupt-controller/arm-gic.h>
- #include <dt-bindings/interrupt-controller/irq.h>
-+#include <dt-bindings/net/qcom-qca807x.h>
- / {
-       #address-cells = <1>;
-@@ -598,22 +599,38 @@
-                       ethphy0: ethernet-phy@0 {
-                               reg = <0>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-                       };
-                       ethphy1: ethernet-phy@1 {
-                               reg = <1>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-                       };
-                       ethphy2: ethernet-phy@2 {
-                               reg = <2>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-                       };
-                       ethphy3: ethernet-phy@3 {
-                               reg = <3>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-                       };
-                       ethphy4: ethernet-phy@4 {
-                               reg = <4>;
-+
-+                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
-+                      };
-+
-+                      psgmiiphy: psgmii-phy@5 {
-+                              reg = <5>;
-+
-+                              qcom,tx-driver-strength = <PSGMII_QSGMII_TX_DRIVER_300MV>;
-                       };
-               };
diff --git a/target/linux/ipq40xx/patches-5.15/708-net-phy-Add-Qualcom-QCA807x-driver.patch b/target/linux/ipq40xx/patches-5.15/708-net-phy-Add-Qualcom-QCA807x-driver.patch
new file mode 100644 (file)
index 0000000..87be598
--- /dev/null
@@ -0,0 +1,50 @@
+From f825cdc8bfde7616a14e2163f16303a8973031d2 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Wed, 7 Oct 2020 17:38:48 +0200
+Subject: [PATCH] net: phy: Add Qualcom QCA807x driver
+
+This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s.
+
+They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te, 100BASE-TX and 1000BASE-T PHY-s.
+
+They feature 2 SerDes, one for PSGMII or QSGMII connection with MAC, while second one is SGMII for connection to MAC or fiber.
+
+Both models have a combo port that supports 1000BASE-X and 100BASE-FX fiber.
+
+Each PHY inside of QCA807x series has 4 digitally controlled output only pins that natively drive LED-s.
+But some vendors used these to driver generic LED-s controlled by userspace,
+so lets enable registering each PHY as GPIO controller and add driver for it.
+
+These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x boards.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ drivers/net/phy/Kconfig  | 6 ++++++
+ drivers/net/phy/Makefile | 1 +
+ 2 files changed, 7 insertions(+)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -346,6 +346,12 @@ config AT803X_PHY
+         Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
+         QCA8337(Internal qca8k PHY) model
++config QCA807X_PHY
++      tristate "Qualcomm QCA807X PHYs"
++      depends on OF_MDIO
++      help
++        Currently supports the QCA8072 and QCA8075 models.
++
+ config QSEMI_PHY
+       tristate "Quality Semiconductor PHYs"
+       help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -92,6 +92,7 @@ obj-$(CONFIG_NATIONAL_PHY)   += national.o
+ obj-$(CONFIG_NXP_C45_TJA11XX_PHY)     += nxp-c45-tja11xx.o
+ obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
+ obj-$(CONFIG_QSEMI_PHY)               += qsemi.o
++obj-$(CONFIG_QCA807X_PHY)             += qca807x.o
+ obj-$(CONFIG_REALTEK_PHY)     += realtek.o
+ obj-$(CONFIG_RENESAS_PHY)     += uPD60620.o
+ obj-$(CONFIG_ROCKCHIP_PHY)    += rockchip.o
diff --git a/target/linux/ipq40xx/patches-5.15/709-arm-dts-ipq4019-QCA807x-properties.patch b/target/linux/ipq40xx/patches-5.15/709-arm-dts-ipq4019-QCA807x-properties.patch
new file mode 100644 (file)
index 0000000..cc4b44b
--- /dev/null
@@ -0,0 +1,61 @@
+From e0fa88eaa3c176b71e563da68949ac2ab45aaa61 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Fri, 2 Oct 2020 10:43:26 +0200
+Subject: [PATCH] arm: dts: ipq4019: QCA807x properties
+
+This adds necessary DT properties for QCA807x PHY-s to IPQ4019 DTSI.
+
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -8,6 +8,7 @@
+ #include <dt-bindings/clock/qcom,gcc-ipq4019.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/net/qcom-qca807x.h>
+ / {
+       #address-cells = <1>;
+@@ -726,22 +727,38 @@
+                       ethphy0: ethernet-phy@0 {
+                               reg = <0>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
+                       };
+                       ethphy1: ethernet-phy@1 {
+                               reg = <1>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
+                       };
+                       ethphy2: ethernet-phy@2 {
+                               reg = <2>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
+                       };
+                       ethphy3: ethernet-phy@3 {
+                               reg = <3>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
+                       };
+                       ethphy4: ethernet-phy@4 {
+                               reg = <4>;
++
++                              qcom,control-dac = <QCA807X_CONTROL_DAC_DSP_VOLT_QUARTER_BIAS>;
++                      };
++
++                      psgmiiphy: psgmii-phy@5 {
++                              reg = <5>;
++
++                              qcom,tx-driver-strength = <PSGMII_QSGMII_TX_DRIVER_300MV>;
+                       };
+               };