diff options
Diffstat (limited to 'target/linux/ipq40xx/patches-6.1/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch')
-rw-r--r-- | target/linux/ipq40xx/patches-6.1/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch | 1132 |
1 files changed, 0 insertions, 1132 deletions
diff --git a/target/linux/ipq40xx/patches-6.1/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch b/target/linux/ipq40xx/patches-6.1/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch deleted file mode 100644 index 992884cf31..0000000000 --- a/target/linux/ipq40xx/patches-6.1/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch +++ /dev/null @@ -1,1132 +0,0 @@ -From a38126870488398932e017dd9d76174b4aadbbbb Mon Sep 17 00:00:00 2001 -From: Robert Marko <robert.marko@sartura.hr> -Date: Sat, 10 Sep 2022 15:46:09 +0200 -Subject: [PATCH] net: dsa: qca8k: add 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 out of -band based tagger is used. - -Signed-off-by: Robert Marko <robert.marko@sartura.hr> ---- - drivers/net/dsa/qca/Kconfig | 8 + - drivers/net/dsa/qca/Makefile | 1 + - drivers/net/dsa/qca/qca8k-common.c | 6 +- - drivers/net/dsa/qca/qca8k-ipq4019.c | 948 ++++++++++++++++++++++++++++ - drivers/net/dsa/qca/qca8k.h | 56 ++ - 5 files changed, 1016 insertions(+), 3 deletions(-) - create mode 100644 drivers/net/dsa/qca/qca8k-ipq4019.c - ---- a/drivers/net/dsa/qca/Kconfig -+++ b/drivers/net/dsa/qca/Kconfig -@@ -23,3 +23,11 @@ config NET_DSA_QCA8K_LEDS_SUPPORT - help - This enabled support for LEDs present on the Qualcomm Atheros - QCA8K Ethernet switch chips. -+ -+config NET_DSA_QCA8K_IPQ4019 -+ tristate "Qualcomm Atheros IPQ4019 Ethernet switch support" -+ select NET_DSA_TAG_OOB -+ select REGMAP_MMIO -+ help -+ This enables support for the switch built-into Qualcomm Atheros -+ IPQ4019 SoCs. ---- a/drivers/net/dsa/qca/Makefile -+++ b/drivers/net/dsa/qca/Makefile -@@ -5,3 +5,4 @@ qca8k-y += qca8k-common.o qca8k-8xxx. - ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT - qca8k-y += qca8k-leds.o - endif -+obj-$(CONFIG_NET_DSA_QCA8K_IPQ4019) += qca8k-ipq4019.o qca8k-common.o ---- a/drivers/net/dsa/qca/qca8k-common.c -+++ b/drivers/net/dsa/qca/qca8k-common.c -@@ -412,7 +412,7 @@ static int qca8k_vlan_del(struct qca8k_p - - /* Check if we're the last member to be removed */ - del = true; -- for (i = 0; i < QCA8K_NUM_PORTS; i++) { -+ for (i = 0; i < priv->ds->num_ports; i++) { - mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); - - if ((reg & mask) != mask) { -@@ -653,7 +653,7 @@ int qca8k_port_bridge_join(struct dsa_sw - cpu_port = dsa_to_port(ds, port)->cpu_dp->index; - port_mask = BIT(cpu_port); - -- for (i = 0; i < QCA8K_NUM_PORTS; i++) { -+ for (i = 0; i < ds->num_ports; i++) { - if (dsa_is_cpu_port(ds, i)) - continue; - if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) -@@ -685,7 +685,7 @@ void qca8k_port_bridge_leave(struct dsa_ - - cpu_port = dsa_to_port(ds, port)->cpu_dp->index; - -- for (i = 0; i < QCA8K_NUM_PORTS; i++) { -+ for (i = 0; i < ds->num_ports; i++) { - if (dsa_is_cpu_port(ds, i)) - continue; - if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) ---- /dev/null -+++ b/drivers/net/dsa/qca/qca8k-ipq4019.c -@@ -0,0 +1,948 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name> -+ * 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) 2022 Robert Marko <robert.marko@sartura.hr> -+ */ -+ -+#include <linux/module.h> -+#include <linux/phy.h> -+#include <linux/netdevice.h> -+#include <linux/bitfield.h> -+#include <linux/regmap.h> -+#include <net/dsa.h> -+#include <linux/of_net.h> -+#include <linux/of_mdio.h> -+#include <linux/of_platform.h> -+#include <linux/mdio.h> -+#include <linux/phylink.h> -+ -+#include "qca8k.h" -+ -+static struct regmap_config qca8k_ipq4019_regmap_config = { -+ .reg_bits = 32, -+ .val_bits = 32, -+ .reg_stride = 4, -+ .max_register = 0x16ac, /* end MIB - Port6 range */ -+ .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 enum dsa_tag_protocol -+qca8k_ipq4019_get_tag_protocol(struct dsa_switch *ds, int port, -+ enum dsa_tag_protocol mp) -+{ -+ return DSA_TAG_PROTO_OOB; -+} -+ -+static struct phylink_pcs * -+qca8k_ipq4019_phylink_mac_select_pcs(struct dsa_switch *ds, int port, -+ phy_interface_t interface) -+{ -+ struct qca8k_priv *priv = ds->priv; -+ struct phylink_pcs *pcs = NULL; -+ -+ switch (interface) { -+ case PHY_INTERFACE_MODE_PSGMII: -+ switch (port) { -+ case 0: -+ pcs = &priv->pcs_port_0.pcs; -+ break; -+ } -+ break; -+ default: -+ break; -+ } -+ -+ return pcs; -+} -+ -+static int qca8k_ipq4019_pcs_config(struct phylink_pcs *pcs, unsigned int mode, -+ phy_interface_t interface, -+ const unsigned long *advertising, -+ bool permit_pause_to_mac) -+{ -+ return 0; -+} -+ -+static void qca8k_ipq4019_pcs_an_restart(struct phylink_pcs *pcs) -+{ -+} -+ -+static struct qca8k_pcs *pcs_to_qca8k_pcs(struct phylink_pcs *pcs) -+{ -+ return container_of(pcs, struct qca8k_pcs, pcs); -+} -+ -+static void qca8k_ipq4019_pcs_get_state(struct phylink_pcs *pcs, -+ struct phylink_link_state *state) -+{ -+ struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv; -+ int port = pcs_to_qca8k_pcs(pcs)->port; -+ u32 reg; -+ int ret; -+ -+ ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); -+ if (ret < 0) { -+ state->link = false; -+ return; -+ } -+ -+ state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); -+ state->an_complete = state->link; -+ state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : -+ DUPLEX_HALF; -+ -+ switch (reg & QCA8K_PORT_STATUS_SPEED) { -+ case QCA8K_PORT_STATUS_SPEED_10: -+ state->speed = SPEED_10; -+ break; -+ case QCA8K_PORT_STATUS_SPEED_100: -+ state->speed = SPEED_100; -+ break; -+ case QCA8K_PORT_STATUS_SPEED_1000: -+ state->speed = SPEED_1000; -+ break; -+ default: -+ state->speed = SPEED_UNKNOWN; -+ break; -+ } -+ -+ if (reg & QCA8K_PORT_STATUS_RXFLOW) -+ state->pause |= MLO_PAUSE_RX; -+ if (reg & QCA8K_PORT_STATUS_TXFLOW) -+ state->pause |= MLO_PAUSE_TX; -+} -+ -+static const struct phylink_pcs_ops qca8k_pcs_ops = { -+ .pcs_get_state = qca8k_ipq4019_pcs_get_state, -+ .pcs_config = qca8k_ipq4019_pcs_config, -+ .pcs_an_restart = qca8k_ipq4019_pcs_an_restart, -+}; -+ -+static void qca8k_ipq4019_setup_pcs(struct qca8k_priv *priv, -+ struct qca8k_pcs *qpcs, -+ int port) -+{ -+ qpcs->pcs.ops = &qca8k_pcs_ops; -+ -+ /* We don't have interrupts for link changes, so we need to poll */ -+ qpcs->pcs.poll = true; -+ qpcs->priv = priv; -+ qpcs->port = port; -+} -+ -+static void qca8k_ipq4019_phylink_get_caps(struct dsa_switch *ds, int port, -+ struct phylink_config *config) -+{ -+ switch (port) { -+ case 0: /* CPU port */ -+ __set_bit(PHY_INTERFACE_MODE_INTERNAL, -+ config->supported_interfaces); -+ break; -+ -+ case 1: -+ case 2: -+ case 3: -+ __set_bit(PHY_INTERFACE_MODE_PSGMII, -+ config->supported_interfaces); -+ break; -+ case 4: -+ case 5: -+ phy_interface_set_rgmii(config->supported_interfaces); -+ __set_bit(PHY_INTERFACE_MODE_PSGMII, -+ config->supported_interfaces); -+ break; -+ } -+ -+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | -+ MAC_10 | MAC_100 | MAC_1000FD; -+ -+ config->legacy_pre_march2020 = false; -+} -+ -+static void -+qca8k_phylink_ipq4019_mac_link_down(struct dsa_switch *ds, int port, -+ unsigned int mode, -+ phy_interface_t interface) -+{ -+ struct qca8k_priv *priv = ds->priv; -+ -+ qca8k_port_set_status(priv, port, 0); -+} -+ -+static void -+qca8k_phylink_ipq4019_mac_link_up(struct dsa_switch *ds, int port, -+ unsigned int mode, phy_interface_t interface, -+ struct phy_device *phydev, int speed, -+ int duplex, bool tx_pause, bool rx_pause) -+{ -+ struct qca8k_priv *priv = ds->priv; -+ u32 reg; -+ -+ if (phylink_autoneg_inband(mode)) { -+ reg = QCA8K_PORT_STATUS_LINK_AUTO; -+ } else { -+ switch (speed) { -+ case SPEED_10: -+ reg = QCA8K_PORT_STATUS_SPEED_10; -+ break; -+ case SPEED_100: -+ reg = QCA8K_PORT_STATUS_SPEED_100; -+ break; -+ case SPEED_1000: -+ reg = QCA8K_PORT_STATUS_SPEED_1000; -+ break; -+ default: -+ reg = QCA8K_PORT_STATUS_LINK_AUTO; -+ break; -+ } -+ -+ if (duplex == DUPLEX_FULL) -+ reg |= QCA8K_PORT_STATUS_DUPLEX; -+ -+ if (rx_pause || dsa_is_cpu_port(ds, port)) -+ reg |= QCA8K_PORT_STATUS_RXFLOW; -+ -+ if (tx_pause || dsa_is_cpu_port(ds, port)) -+ reg |= QCA8K_PORT_STATUS_TXFLOW; -+ } -+ -+ reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; -+ -+ qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); -+} -+ -+static int psgmii_vco_calibrate(struct qca8k_priv *priv) -+{ -+ int val, ret; -+ -+ if (!priv->psgmii_ethphy) { -+ dev_err(priv->dev, "PSGMII eth PHY missing, calibration failed!\n"); -+ return -ENODEV; -+ } -+ -+ /* Fix PSGMII RX 20bit */ -+ ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x5b); -+ /* Reset PHY PSGMII */ -+ ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x1b); -+ /* Release PHY PSGMII reset */ -+ ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x5b); -+ -+ /* Poll for VCO PLL calibration finish - Malibu(QCA8075) */ -+ ret = phy_read_mmd_poll_timeout(priv->psgmii_ethphy, -+ MDIO_MMD_PMAPMD, -+ 0x28, val, -+ (val & BIT(0)), -+ 10000, 1000000, -+ false); -+ if (ret) { -+ dev_err(priv->dev, "QCA807x PSGMII VCO calibration PLL not ready\n"); -+ return ret; -+ } -+ mdelay(50); -+ -+ /* Freeze PSGMII RX CDR */ -+ ret = phy_write(priv->psgmii_ethphy, MII_RESV2, 0x2230); -+ -+ /* 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 - Dakota(IPQ40xx) */ -+ 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(priv->dev, "IPQ PSGMIIPHY VCO calibration PLL not ready\n"); -+ return ret; -+ } -+ mdelay(50); -+ -+ /* 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); -+ mdelay(200); -+ -+ return ret; -+} -+ -+static void -+qca8k_switch_port_loopback_on_off(struct qca8k_priv *priv, int port, int on) -+{ -+ u32 val = QCA8K_PORT_LOOKUP_LOOPBACK_EN; -+ -+ if (on == 0) -+ val = 0; -+ -+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), -+ QCA8K_PORT_LOOKUP_LOOPBACK_EN, val); -+} -+ -+static int -+qca8k_wait_for_phy_link_state(struct phy_device *phy, int need_status) -+{ -+ int a; -+ u16 status; -+ -+ for (a = 0; a < 100; a++) { -+ status = phy_read(phy, MII_QCA8075_SSTATUS); -+ status &= QCA8075_PHY_SPEC_STATUS_LINK; -+ status = !!status; -+ if (status == need_status) -+ return 0; -+ mdelay(8); -+ } -+ -+ return -1; -+} -+ -+static void -+qca8k_phy_loopback_on_off(struct qca8k_priv *priv, struct phy_device *phy, -+ int sw_port, int on) -+{ -+ if (on) { -+ phy_write(phy, MII_BMCR, BMCR_ANENABLE | BMCR_RESET); -+ phy_modify(phy, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN); -+ qca8k_wait_for_phy_link_state(phy, 0); -+ qca8k_write(priv, QCA8K_REG_PORT_STATUS(sw_port), 0); -+ phy_write(phy, MII_BMCR, -+ BMCR_SPEED1000 | -+ BMCR_FULLDPLX | -+ BMCR_LOOPBACK); -+ qca8k_wait_for_phy_link_state(phy, 1); -+ qca8k_write(priv, QCA8K_REG_PORT_STATUS(sw_port), -+ QCA8K_PORT_STATUS_SPEED_1000 | -+ QCA8K_PORT_STATUS_TXMAC | -+ QCA8K_PORT_STATUS_RXMAC | -+ QCA8K_PORT_STATUS_DUPLEX); -+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(sw_port), -+ QCA8K_PORT_LOOKUP_STATE_FORWARD, -+ QCA8K_PORT_LOOKUP_STATE_FORWARD); -+ } else { /* off */ -+ qca8k_write(priv, QCA8K_REG_PORT_STATUS(sw_port), 0); -+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(sw_port), -+ QCA8K_PORT_LOOKUP_STATE_DISABLED, -+ QCA8K_PORT_LOOKUP_STATE_DISABLED); -+ phy_write(phy, MII_BMCR, BMCR_SPEED1000 | BMCR_ANENABLE | BMCR_RESET); -+ /* turn off the power of the phys - so that unused -+ ports do not raise links */ -+ phy_modify(phy, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN); -+ } -+} -+ -+static void -+qca8k_phy_pkt_gen_prep(struct qca8k_priv *priv, struct phy_device *phy, -+ int pkts_num, int on) -+{ -+ if (on) { -+ /* enable CRC checker and packets counters */ -+ phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_CRC_AND_PKTS_COUNT, 0); -+ phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_CRC_AND_PKTS_COUNT, -+ QCA8075_MMD7_CNT_FRAME_CHK_EN | QCA8075_MMD7_CNT_SELFCLR); -+ qca8k_wait_for_phy_link_state(phy, 1); -+ /* packet number */ -+ phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_PKT_NUMB, pkts_num); -+ /* pkt size - 1504 bytes + 20 bytes */ -+ phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_PKT_SIZE, 1504); -+ } else { /* off */ -+ /* packet number */ -+ phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_PKT_NUMB, 0); -+ /* disable CRC checker and packet counter */ -+ phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_CRC_AND_PKTS_COUNT, 0); -+ /* disable traffic gen */ -+ phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_CTRL, 0); -+ } -+} -+ -+static void -+qca8k_wait_for_phy_pkt_gen_fin(struct qca8k_priv *priv, struct phy_device *phy) -+{ -+ int val; -+ /* wait for all traffic end: 4096(pkt num)*1524(size)*8ns(125MHz)=49938us */ -+ phy_read_mmd_poll_timeout(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_CTRL, -+ val, !(val & QCA8075_MMD7_PKT_GEN_INPROGR), -+ 50000, 1000000, true); -+} -+ -+static void -+qca8k_start_phy_pkt_gen(struct phy_device *phy) -+{ -+ /* start traffic gen */ -+ phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_CTRL, -+ QCA8075_MMD7_PKT_GEN_START | QCA8075_MMD7_PKT_GEN_INPROGR); -+} -+ -+static int -+qca8k_start_all_phys_pkt_gens(struct qca8k_priv *priv) -+{ -+ struct phy_device *phy; -+ phy = phy_device_create(priv->bus, QCA8075_MDIO_BRDCST_PHY_ADDR, -+ 0, 0, NULL); -+ if (!phy) { -+ dev_err(priv->dev, "unable to create mdio broadcast PHY(0x%x)\n", -+ QCA8075_MDIO_BRDCST_PHY_ADDR); -+ return -ENODEV; -+ } -+ -+ qca8k_start_phy_pkt_gen(phy); -+ -+ phy_device_free(phy); -+ return 0; -+} -+ -+static int -+qca8k_get_phy_pkt_gen_test_result(struct phy_device *phy, int pkts_num) -+{ -+ u32 tx_ok, tx_error; -+ u32 rx_ok, rx_error; -+ u32 tx_ok_high16; -+ u32 rx_ok_high16; -+ u32 tx_all_ok, rx_all_ok; -+ -+ /* check counters */ -+ tx_ok = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_EG_FRAME_RECV_CNT_LO); -+ tx_ok_high16 = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_EG_FRAME_RECV_CNT_HI); -+ tx_error = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_EG_FRAME_ERR_CNT); -+ rx_ok = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_IG_FRAME_RECV_CNT_LO); -+ rx_ok_high16 = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_IG_FRAME_RECV_CNT_HI); -+ rx_error = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_IG_FRAME_ERR_CNT); -+ tx_all_ok = tx_ok + (tx_ok_high16 << 16); -+ rx_all_ok = rx_ok + (rx_ok_high16 << 16); -+ -+ if (tx_all_ok < pkts_num) -+ return -1; -+ if(rx_all_ok < pkts_num) -+ return -2; -+ if(tx_error) -+ return -3; -+ if(rx_error) -+ return -4; -+ return 0; /* test is ok */ -+} -+ -+static -+void qca8k_phy_broadcast_write_on_off(struct qca8k_priv *priv, -+ struct phy_device *phy, int on) -+{ -+ u32 val; -+ -+ val = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_MDIO_BRDCST_WRITE); -+ -+ if (on == 0) -+ val &= ~QCA8075_MMD7_MDIO_BRDCST_WRITE_EN; -+ else -+ val |= QCA8075_MMD7_MDIO_BRDCST_WRITE_EN; -+ -+ phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_MDIO_BRDCST_WRITE, val); -+} -+ -+static int -+qca8k_test_dsa_port_for_errors(struct qca8k_priv *priv, struct phy_device *phy, -+ int port, int test_phase) -+{ -+ int res = 0; -+ const int test_pkts_num = QCA8075_PKT_GEN_PKTS_COUNT; -+ -+ if (test_phase == 1) { /* start test preps */ -+ qca8k_phy_loopback_on_off(priv, phy, port, 1); -+ qca8k_switch_port_loopback_on_off(priv, port, 1); -+ qca8k_phy_broadcast_write_on_off(priv, phy, 1); -+ qca8k_phy_pkt_gen_prep(priv, phy, test_pkts_num, 1); -+ } else if (test_phase == 2) { -+ /* wait for test results, collect it and cleanup */ -+ qca8k_wait_for_phy_pkt_gen_fin(priv, phy); -+ res = qca8k_get_phy_pkt_gen_test_result(phy, test_pkts_num); -+ qca8k_phy_pkt_gen_prep(priv, phy, test_pkts_num, 0); -+ qca8k_phy_broadcast_write_on_off(priv, phy, 0); -+ qca8k_switch_port_loopback_on_off(priv, port, 0); -+ qca8k_phy_loopback_on_off(priv, phy, port, 0); -+ } -+ -+ return res; -+} -+ -+static int -+qca8k_do_dsa_sw_ports_self_test(struct qca8k_priv *priv, int parallel_test) -+{ -+ struct device_node *dn = priv->dev->of_node; -+ struct device_node *ports, *port; -+ struct device_node *phy_dn; -+ struct phy_device *phy; -+ int reg, err = 0, test_phase; -+ u32 tests_result = 0; -+ -+ ports = of_get_child_by_name(dn, "ports"); -+ if (!ports) { -+ dev_err(priv->dev, "no ports child node found\n"); -+ return -EINVAL; -+ } -+ -+ for (test_phase = 1; test_phase <= 2; test_phase++) { -+ if (parallel_test && test_phase == 2) { -+ err = qca8k_start_all_phys_pkt_gens(priv); -+ if (err) -+ goto error; -+ } -+ for_each_available_child_of_node(ports, port) { -+ err = of_property_read_u32(port, "reg", ®); -+ if (err) -+ goto error; -+ if (reg >= QCA8K_NUM_PORTS) { -+ err = -EINVAL; -+ goto error; -+ } -+ phy_dn = of_parse_phandle(port, "phy-handle", 0); -+ if (phy_dn) { -+ phy = of_phy_find_device(phy_dn); -+ of_node_put(phy_dn); -+ if (phy) { -+ int result; -+ result = qca8k_test_dsa_port_for_errors(priv, -+ phy, reg, test_phase); -+ if (!parallel_test && test_phase == 1) -+ qca8k_start_phy_pkt_gen(phy); -+ put_device(&phy->mdio.dev); -+ if (test_phase == 2) { -+ tests_result <<= 1; -+ if (result) -+ tests_result |= 1; -+ } -+ } -+ } -+ } -+ } -+ -+end: -+ of_node_put(ports); -+ qca8k_fdb_flush(priv); -+ return tests_result; -+error: -+ tests_result |= 0xf000; -+ goto end; -+} -+ -+static int -+psgmii_vco_calibrate_and_test(struct dsa_switch *ds) -+{ -+ int ret, a, test_result; -+ struct qca8k_priv *priv = ds->priv; -+ -+ for (a = 0; a <= QCA8K_PSGMII_CALB_NUM; a++) { -+ ret = psgmii_vco_calibrate(priv); -+ if (ret) -+ return ret; -+ /* first we run serial test */ -+ test_result = qca8k_do_dsa_sw_ports_self_test(priv, 0); -+ /* and if it is ok then we run the test in parallel */ -+ if (!test_result) -+ test_result = qca8k_do_dsa_sw_ports_self_test(priv, 1); -+ if (!test_result) { -+ if (a > 0) { -+ dev_warn(priv->dev, "PSGMII work was stabilized after %d " -+ "calibration retries !\n", a); -+ } -+ return 0; -+ } else { -+ schedule(); -+ if (a > 0 && a % 10 == 0) { -+ dev_err(priv->dev, "PSGMII work is unstable !!! " -+ "Let's try to wait a bit ... %d\n", a); -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(msecs_to_jiffies(a * 100)); -+ } -+ } -+ } -+ -+ panic("PSGMII work is unstable !!! " -+ "Repeated recalibration attempts did not help(0x%x) !\n", -+ test_result); -+ -+ return -EFAULT; -+} -+ -+static int -+ipq4019_psgmii_configure(struct dsa_switch *ds) -+{ -+ struct qca8k_priv *priv = ds->priv; -+ int ret; -+ -+ if (!priv->psgmii_calibrated) { -+ dev_info(ds->dev, "PSGMII calibration!\n"); -+ ret = psgmii_vco_calibrate_and_test(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 -+qca8k_phylink_ipq4019_mac_config(struct dsa_switch *ds, int port, -+ unsigned int mode, -+ const struct phylink_link_state *state) -+{ -+ struct qca8k_priv *priv = ds->priv; -+ -+ switch (port) { -+ 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: -+ 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) { -+ regmap_set_bits(priv->regmap, -+ QCA8K_IPQ4019_REG_RGMII_CTRL, -+ QCA8K_IPQ4019_RGMII_CTRL_CLK); -+ } -+ -+ 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, "%s: unsupported port: %i\n", __func__, port); -+ return; -+ } -+} -+ -+static int -+qca8k_ipq4019_setup_port(struct dsa_switch *ds, int port) -+{ -+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; -+ int ret; -+ -+ /* 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(port), -+ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); -+ if (ret) -+ return ret; -+ -+ /* Disable CPU ARP Auto-learning by default */ -+ ret = regmap_clear_bits(priv->regmap, -+ QCA8K_PORT_LOOKUP_CTRL(port), -+ QCA8K_PORT_LOOKUP_LEARN); -+ if (ret) -+ return ret; -+ } -+ -+ /* Individual user ports get connected to CPU port only */ -+ if (dsa_is_user_port(ds, port)) { -+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), -+ QCA8K_PORT_LOOKUP_MEMBER, -+ BIT(QCA8K_IPQ4019_CPU_PORT)); -+ if (ret) -+ return ret; -+ -+ /* Enable ARP Auto-learning by default */ -+ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), -+ 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(port), -+ QCA8K_EGREES_VLAN_PORT_MASK(port), -+ QCA8K_EGREES_VLAN_PORT(port, QCA8K_PORT_VID_DEF)); -+ if (ret) -+ return ret; -+ -+ 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; -+} -+ -+static int -+qca8k_ipq4019_setup(struct dsa_switch *ds) -+{ -+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; -+ int ret, i; -+ -+ /* Make sure that port 0 is the cpu port */ -+ if (!dsa_is_cpu_port(ds, QCA8K_IPQ4019_CPU_PORT)) { -+ dev_err(priv->dev, "port %d is not the CPU port", -+ QCA8K_IPQ4019_CPU_PORT); -+ return -EINVAL; -+ } -+ -+ qca8k_ipq4019_setup_pcs(priv, &priv->pcs_port_0, 0); -+ -+ /* Enable CPU Port */ -+ ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, -+ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); -+ if (ret) { -+ dev_err(priv->dev, "failed enabling CPU port"); -+ return ret; -+ } -+ -+ /* Enable MIB counters */ -+ ret = qca8k_mib_init(priv); -+ if (ret) -+ dev_warn(priv->dev, "MIB init failed"); -+ -+ /* Disable forwarding by default on all ports */ -+ for (i = 0; i < QCA8K_IPQ4019_NUM_PORTS; i++) { -+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), -+ QCA8K_PORT_LOOKUP_MEMBER, 0); -+ if (ret) -+ return ret; -+ } -+ -+ /* Enable QCA header mode on the CPU port */ -+ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_IPQ4019_CPU_PORT), -+ FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | -+ FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); -+ if (ret) { -+ dev_err(priv->dev, "failed enabling QCA header mode"); -+ return ret; -+ } -+ -+ /* Disable MAC by default on all ports */ -+ for (i = 0; i < QCA8K_IPQ4019_NUM_PORTS; i++) { -+ if (dsa_is_user_port(ds, 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, -+ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(QCA8K_IPQ4019_CPU_PORT)) | -+ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(QCA8K_IPQ4019_CPU_PORT)) | -+ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(QCA8K_IPQ4019_CPU_PORT)) | -+ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(QCA8K_IPQ4019_CPU_PORT))); -+ if (ret) -+ return ret; -+ -+ /* Setup connection between CPU port & user ports */ -+ for (i = 0; i < QCA8K_IPQ4019_NUM_PORTS; i++) { -+ ret = qca8k_ipq4019_setup_port(ds, i); -+ if (ret) -+ return ret; -+ } -+ -+ /* Setup our port MTUs to match power on defaults */ -+ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); -+ if (ret) -+ dev_warn(priv->dev, "failed setting MTU settings"); -+ -+ /* Flush the FDB table */ -+ qca8k_fdb_flush(priv); -+ -+ /* Set min a max ageing value supported */ -+ ds->ageing_time_min = 7000; -+ ds->ageing_time_max = 458745000; -+ -+ /* Set max number of LAGs supported */ -+ ds->num_lag_ids = QCA8K_NUM_LAGS; -+ -+ /* CPU port HW learning doesnt work correctly, so let DSA handle it */ -+ ds->assisted_learning_on_cpu_port = true; -+ -+ return 0; -+} -+ -+static const struct dsa_switch_ops qca8k_ipq4019_switch_ops = { -+ .get_tag_protocol = qca8k_ipq4019_get_tag_protocol, -+ .setup = qca8k_ipq4019_setup, -+ .get_strings = qca8k_get_strings, -+ .get_ethtool_stats = qca8k_get_ethtool_stats, -+ .get_sset_count = qca8k_get_sset_count, -+ .set_ageing_time = qca8k_set_ageing_time, -+ .get_mac_eee = qca8k_get_mac_eee, -+ .set_mac_eee = qca8k_set_mac_eee, -+ .port_enable = qca8k_port_enable, -+ .port_disable = qca8k_port_disable, -+ .port_change_mtu = qca8k_port_change_mtu, -+ .port_max_mtu = qca8k_port_max_mtu, -+ .port_stp_state_set = qca8k_port_stp_state_set, -+ .port_bridge_join = qca8k_port_bridge_join, -+ .port_bridge_leave = qca8k_port_bridge_leave, -+ .port_fast_age = qca8k_port_fast_age, -+ .port_fdb_add = qca8k_port_fdb_add, -+ .port_fdb_del = qca8k_port_fdb_del, -+ .port_fdb_dump = qca8k_port_fdb_dump, -+ .port_mdb_add = qca8k_port_mdb_add, -+ .port_mdb_del = qca8k_port_mdb_del, -+ .port_mirror_add = qca8k_port_mirror_add, -+ .port_mirror_del = qca8k_port_mirror_del, -+ .port_vlan_filtering = qca8k_port_vlan_filtering, -+ .port_vlan_add = qca8k_port_vlan_add, -+ .port_vlan_del = qca8k_port_vlan_del, -+ .phylink_mac_select_pcs = qca8k_ipq4019_phylink_mac_select_pcs, -+ .phylink_get_caps = qca8k_ipq4019_phylink_get_caps, -+ .phylink_mac_config = qca8k_phylink_ipq4019_mac_config, -+ .phylink_mac_link_down = qca8k_phylink_ipq4019_mac_link_down, -+ .phylink_mac_link_up = qca8k_phylink_ipq4019_mac_link_up, -+ .port_lag_join = qca8k_port_lag_join, -+ .port_lag_leave = qca8k_port_lag_leave, -+}; -+ -+static const struct qca8k_match_data ipq4019 = { -+ .id = QCA8K_ID_IPQ4019, -+ .mib_count = QCA8K_QCA833X_MIB_COUNT, -+}; -+ -+static int -+qca8k_ipq4019_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct qca8k_priv *priv; -+ void __iomem *base, *psgmii; -+ struct device_node *np = dev->of_node, *mdio_np, *psgmii_ethphy_np; -+ int ret; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ priv->dev = dev; -+ priv->info = &ipq4019; -+ -+ /* Start by setting up the register mapping */ -+ base = devm_platform_ioremap_resource_byname(pdev, "base"); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); -+ -+ priv->regmap = devm_regmap_init_mmio(dev, base, -+ &qca8k_ipq4019_regmap_config); -+ if (IS_ERR(priv->regmap)) { -+ ret = PTR_ERR(priv->regmap); -+ dev_err(dev, "base regmap initialization failed, %d\n", ret); -+ return ret; -+ } -+ -+ psgmii = devm_platform_ioremap_resource_byname(pdev, "psgmii_phy"); -+ if (IS_ERR(psgmii)) -+ return PTR_ERR(psgmii); -+ -+ priv->psgmii = devm_regmap_init_mmio(dev, psgmii, -+ &qca8k_ipq4019_psgmii_phy_regmap_config); -+ if (IS_ERR(priv->psgmii)) { -+ ret = PTR_ERR(priv->psgmii); -+ dev_err(dev, "PSGMII regmap initialization failed, %d\n", ret); -+ return ret; -+ } -+ -+ mdio_np = of_parse_phandle(np, "mdio", 0); -+ if (!mdio_np) { -+ dev_err(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(dev, "unable to find MDIO bus\n"); -+ return -EPROBE_DEFER; -+ } -+ -+ psgmii_ethphy_np = of_parse_phandle(np, "psgmii-ethphy", 0); -+ if (!psgmii_ethphy_np) { -+ dev_dbg(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(dev, "unable to get PSGMII eth PHY\n"); -+ return -ENODEV; -+ } -+ } -+ -+ /* Check the detected switch id */ -+ ret = qca8k_read_switch_id(priv); -+ if (ret) -+ return ret; -+ -+ priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); -+ if (!priv->ds) -+ return -ENOMEM; -+ -+ priv->ds->dev = dev; -+ priv->ds->num_ports = QCA8K_IPQ4019_NUM_PORTS; -+ priv->ds->priv = priv; -+ priv->ds->ops = &qca8k_ipq4019_switch_ops; -+ mutex_init(&priv->reg_mutex); -+ platform_set_drvdata(pdev, priv); -+ -+ return dsa_register_switch(priv->ds); -+} -+ -+static int -+qca8k_ipq4019_remove(struct platform_device *pdev) -+{ -+ struct qca8k_priv *priv = dev_get_drvdata(&pdev->dev); -+ int i; -+ -+ if (!priv) -+ return 0; -+ -+ for (i = 0; i < QCA8K_IPQ4019_NUM_PORTS; i++) -+ qca8k_port_set_status(priv, i, 0); -+ -+ dsa_unregister_switch(priv->ds); -+ -+ platform_set_drvdata(pdev, NULL); -+ -+ return 0; -+} -+ -+static const struct of_device_id qca8k_ipq4019_of_match[] = { -+ { .compatible = "qca,ipq4019-qca8337n", }, -+ { /* sentinel */ }, -+}; -+ -+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, -+ }, -+}; -+ -+module_platform_driver(qca8k_ipq4019_driver); -+ -+MODULE_AUTHOR("Mathieu Olivari, John Crispin <john@phrozen.org>"); -+MODULE_AUTHOR("Gabor Juhos <j4g8y7@gmail.com>, Robert Marko <robert.marko@sartura.hr>"); -+MODULE_DESCRIPTION("Qualcomm IPQ4019 built-in switch driver"); -+MODULE_LICENSE("GPL"); ---- a/drivers/net/dsa/qca/qca8k.h -+++ b/drivers/net/dsa/qca/qca8k.h -@@ -19,7 +19,10 @@ - #define QCA8K_ETHERNET_TIMEOUT 5 - - #define QCA8K_NUM_PORTS 7 -+#define QCA8K_IPQ4019_NUM_PORTS 6 - #define QCA8K_NUM_CPU_PORTS 2 -+#define QCA8K_IPQ4019_NUM_CPU_PORTS 1 -+#define QCA8K_IPQ4019_CPU_PORT 0 - #define QCA8K_MAX_MTU 9000 - #define QCA8K_NUM_LAGS 4 - #define QCA8K_NUM_PORTS_FOR_LAG 4 -@@ -28,6 +31,7 @@ - #define QCA8K_ID_QCA8327 0x12 - #define PHY_ID_QCA8337 0x004dd036 - #define QCA8K_ID_QCA8337 0x13 -+#define QCA8K_ID_IPQ4019 0x14 - - #define QCA8K_QCA832X_MIB_COUNT 39 - #define QCA8K_QCA833X_MIB_COUNT 41 -@@ -265,6 +269,7 @@ - #define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3) - #define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4) - #define QCA8K_PORT_LOOKUP_LEARN BIT(20) -+#define QCA8K_PORT_LOOKUP_LOOPBACK_EN BIT(21) - #define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25) - - #define QCA8K_REG_GOL_TRUNK_CTRL0 0x700 -@@ -341,6 +346,53 @@ - #define MII_ATH_MMD_ADDR 0x0d - #define MII_ATH_MMD_DATA 0x0e - -+/* IPQ4019 PSGMII PHY registers */ -+#define QCA8K_IPQ4019_REG_RGMII_CTRL 0x004 -+#define QCA8K_IPQ4019_RGMII_CTRL_RGMII_RXC GENMASK(1, 0) -+#define QCA8K_IPQ4019_RGMII_CTRL_RGMII_TXC GENMASK(9, 8) -+/* Some kind of CLK selection -+ * 0: gcc_ess_dly2ns -+ * 1: gcc_ess_clk -+ */ -+#define QCA8K_IPQ4019_RGMII_CTRL_CLK BIT(10) -+#define QCA8K_IPQ4019_RGMII_CTRL_DELAY_RMII0 GENMASK(17, 16) -+#define QCA8K_IPQ4019_RGMII_CTRL_INVERT_RMII0_REF_CLK BIT(18) -+#define QCA8K_IPQ4019_RGMII_CTRL_DELAY_RMII1 GENMASK(20, 19) -+#define QCA8K_IPQ4019_RGMII_CTRL_INVERT_RMII1_REF_CLK BIT(21) -+#define QCA8K_IPQ4019_RGMII_CTRL_INVERT_RMII0_MASTER_EN BIT(24) -+#define QCA8K_IPQ4019_RGMII_CTRL_INVERT_RMII1_MASTER_EN BIT(25) -+ -+#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) -+ -+#define QCA8K_PSGMII_CALB_NUM 100 -+#define MII_QCA8075_SSTATUS 0x11 -+#define QCA8075_PHY_SPEC_STATUS_LINK BIT(10) -+#define QCA8075_MMD7_CRC_AND_PKTS_COUNT 0x8029 -+#define QCA8075_MMD7_PKT_GEN_PKT_NUMB 0x8021 -+#define QCA8075_MMD7_PKT_GEN_PKT_SIZE 0x8062 -+#define QCA8075_MMD7_PKT_GEN_CTRL 0x8020 -+#define QCA8075_MMD7_CNT_SELFCLR BIT(1) -+#define QCA8075_MMD7_CNT_FRAME_CHK_EN BIT(0) -+#define QCA8075_MMD7_PKT_GEN_START BIT(13) -+#define QCA8075_MMD7_PKT_GEN_INPROGR BIT(15) -+#define QCA8075_MMD7_IG_FRAME_RECV_CNT_HI 0x802a -+#define QCA8075_MMD7_IG_FRAME_RECV_CNT_LO 0x802b -+#define QCA8075_MMD7_IG_FRAME_ERR_CNT 0x802c -+#define QCA8075_MMD7_EG_FRAME_RECV_CNT_HI 0x802d -+#define QCA8075_MMD7_EG_FRAME_RECV_CNT_LO 0x802e -+#define QCA8075_MMD7_EG_FRAME_ERR_CNT 0x802f -+#define QCA8075_MMD7_MDIO_BRDCST_WRITE 0x8028 -+#define QCA8075_MMD7_MDIO_BRDCST_WRITE_EN BIT(15) -+#define QCA8075_MDIO_BRDCST_PHY_ADDR 0x1f -+#define QCA8075_PKT_GEN_PKTS_COUNT 4096 -+ - enum { - QCA8K_PORT_SPEED_10M = 0, - QCA8K_PORT_SPEED_100M = 1, -@@ -466,6 +518,10 @@ struct qca8k_priv { - struct qca8k_pcs pcs_port_6; - const struct qca8k_match_data *info; - struct qca8k_led ports_led[QCA8K_LED_COUNT]; -+ /* IPQ4019 specific */ -+ struct regmap *psgmii; -+ struct phy_device *psgmii_ethphy; -+ bool psgmii_calibrated; - }; - - struct qca8k_mib_desc { |