[PATCH 2/2] net: phy: add Qualcomm QCA807x driver
Robert Marko
robert.marko at sartura.hr
Wed May 22 12:06:16 CEST 2024
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 (Currently not supported in U-Boot)
Each PHY inside of QCA807x series has 2 digitally controlled output only
pins that natively drive LED-s.
These PHY-s are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x
boards.
Signed-off-by: Robert Marko <robert.marko at sartura.hr>
---
drivers/net/phy/Kconfig | 6 ++
drivers/net/phy/Makefile | 1 +
drivers/net/phy/qca807x.c | 211 ++++++++++++++++++++++++++++++++++++++
3 files changed, 218 insertions(+)
create mode 100644 drivers/net/phy/qca807x.c
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 3d96938eab..cc79043b97 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -243,6 +243,12 @@ config PHY_NXP_TJA11XX
help
Currently supports the NXP TJA1100 and TJA1101 PHY.
+config PHY_QCA807X
+ bool "Qualcomm QCA807x PHYs support"
+ help
+ Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII
+ control PHY.
+
config PHY_REALTEK
bool "Realtek Ethernet PHYs support"
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 2487f366e1..b2d31fe4f2 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_PHY_MOTORCOMM) += motorcomm.o
obj-$(CONFIG_PHY_NATSEMI) += natsemi.o
obj-$(CONFIG_PHY_NXP_C45_TJA11XX) += nxp-c45-tja11xx.o
obj-$(CONFIG_PHY_NXP_TJA11XX) += nxp-tja11xx.o
+obj-$(CONFIG_PHY_QCA807X) += qca807x.o
obj-$(CONFIG_PHY_REALTEK) += realtek.o
obj-$(CONFIG_PHY_SMSC) += smsc.o
obj-$(CONFIG_PHY_TERANETICS) += teranetics.o
diff --git a/drivers/net/phy/qca807x.c b/drivers/net/phy/qca807x.c
new file mode 100644
index 0000000000..a851ff34a0
--- /dev/null
+++ b/drivers/net/phy/qca807x.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2024 Sartura Ltd.
+ *
+ * Author: Robert Marko <robert.marko at sartura.hr>
+ *
+ * Qualcomm QCA8072 and QCA8075 PHY driver
+ */
+
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <phy.h>
+
+#define PHY_ID_QCA8072 0x004dd0b2
+#define PHY_ID_QCA8075 0x004dd0b1
+#define PHY_ID_QCA807X_PSGMII 0x06820805
+#define PHY_ID_QCA807X_MASK GENMASK(31, 0)
+
+#define QCA807X_CHIP_CONFIGURATION 0x1f
+#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a
+#define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0)
+/* List of tweaks enabled by this bit:
+ * - With both FULL amplitude and FULL bias current: bias current
+ * is set to half.
+ * - With only DSP amplitude: bias current is set to half and
+ * is set to 1/4 with cable < 10m.
+ * - With DSP bias current (included both DSP amplitude and
+ * DSP bias current): bias current is half the detected current
+ * with cable < 10m.
+ */
+#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK BIT(2)
+#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT BIT(1)
+#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE BIT(0)
+
+#define QCA807X_MMD7_LED_100N_1 0x8074
+#define QCA807X_MMD7_LED_100N_2 0x8075
+#define QCA807X_MMD7_LED_1000N_1 0x8076
+#define QCA807X_MMD7_LED_1000N_2 0x8077
+#define QCA807X_LED_TXACT_BLK_EN_2 BIT(10)
+#define QCA807X_LED_RXACT_BLK_EN_2 BIT(9)
+#define QCA807X_LED_GT_ON_EN_2 BIT(6)
+#define QCA807X_LED_HT_ON_EN_2 BIT(5)
+#define QCA807X_LED_BT_ON_EN_2 BIT(4)
+
+/* PSGMII PHY specific */
+#define PSGMII_QSGMII_DRIVE_CONTROL_1 0xb
+#define PSGMII_QSGMII_TX_DRIVER_MASK GENMASK(7, 4)
+#define PQSGMII_TX_DRIVER_140MV 0x0
+#define PQSGMII_TX_DRIVER_160MV 0x1
+#define PQSGMII_TX_DRIVER_180MV 0x2
+#define PQSGMII_TX_DRIVER_200MV 0x3
+#define PQSGMII_TX_DRIVER_220MV 0x4
+#define PQSGMII_TX_DRIVER_240MV 0x5
+#define PQSGMII_TX_DRIVER_260MV 0x6
+#define PQSGMII_TX_DRIVER_280MV 0x7
+#define PQSGMII_TX_DRIVER_300MV 0x8
+#define PQSGMII_TX_DRIVER_320MV 0x9
+#define PQSGMII_TX_DRIVER_400MV 0xa
+#define PQSGMII_TX_DRIVER_500MV 0xb
+#define PQSGMII_TX_DRIVER_600MV 0xc
+#define PSGMII_MODE_CTRL 0x6d
+#define PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK GENMASK(3, 0)
+#define PSGMII_MMD3_SERDES_CONTROL 0x805a
+
+static int qca807x_config(struct phy_device *phydev)
+{
+ int control_dac, ret = 0;
+ bool dac_full_amplitude;
+ bool dac_full_bias_current;
+ bool dac_disable_bias_current_tweak;
+ ofnode node;
+
+ node = phy_get_ofnode(phydev);
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ dac_full_amplitude = ofnode_read_bool(node, "qcom,dac-full-amplitude");
+ dac_full_bias_current = ofnode_read_bool(node, "qcom,dac-full-bias-current");
+ dac_disable_bias_current_tweak = ofnode_read_bool(node, "qcom,dac-disable-bias-current-tweak");
+
+ /* Check for Combo port */
+ if(phy_read(phydev, MDIO_DEVAD_NONE, QCA807X_CHIP_CONFIGURATION)) {
+ int psgmii_serdes;
+
+ /* Prevent PSGMII going into hibernation via PSGMII self test */
+ psgmii_serdes = phy_read_mmd(phydev, MDIO_MMD_PCS, PSGMII_MMD3_SERDES_CONTROL);
+ psgmii_serdes &= ~BIT(1);
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, PSGMII_MMD3_SERDES_CONTROL, psgmii_serdes);
+ }
+
+ control_dac = phy_read_mmd(phydev, MDIO_MMD_AN, QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH);
+ control_dac &= ~QCA807X_CONTROL_DAC_MASK;
+ if (!dac_full_amplitude)
+ control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE;
+ if (!dac_full_amplitude)
+ control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT;
+ if (!dac_disable_bias_current_tweak)
+ control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK;
+ ret = phy_write_mmd(phydev, MDIO_MMD_AN, QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH, control_dac);
+
+ phydev->supported = phydev->drv->features;
+ phydev->advertising = phydev->drv->features;
+
+ genphy_config_aneg(phydev);
+ genphy_restart_aneg(phydev);
+
+ return ret;
+}
+
+static int qca807x_psgmii_config(struct phy_device *phydev)
+{
+ int psgmii_az, tx_amp, ret = 0;
+ u32 tx_drive_strength;
+ ofnode node;
+
+ node = phy_get_ofnode(phydev);
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ /* Default to 600mw if not defined */
+ if (ofnode_read_u32(node, "qcom,tx-drive-strength-milliwatt",
+ &tx_drive_strength))
+ tx_drive_strength = 600;
+
+ switch (tx_drive_strength) {
+ case 140:
+ tx_drive_strength = PQSGMII_TX_DRIVER_140MV;
+ break;
+ case 160:
+ tx_drive_strength = PQSGMII_TX_DRIVER_160MV;
+ break;
+ case 180:
+ tx_drive_strength = PQSGMII_TX_DRIVER_180MV;
+ break;
+ case 200:
+ tx_drive_strength = PQSGMII_TX_DRIVER_200MV;
+ break;
+ case 220:
+ tx_drive_strength = PQSGMII_TX_DRIVER_220MV;
+ break;
+ case 240:
+ tx_drive_strength = PQSGMII_TX_DRIVER_240MV;
+ break;
+ case 260:
+ tx_drive_strength = PQSGMII_TX_DRIVER_260MV;
+ break;
+ case 280:
+ tx_drive_strength = PQSGMII_TX_DRIVER_280MV;
+ break;
+ case 300:
+ tx_drive_strength = PQSGMII_TX_DRIVER_300MV;
+ break;
+ case 320:
+ tx_drive_strength = PQSGMII_TX_DRIVER_320MV;
+ break;
+ case 400:
+ tx_drive_strength = PQSGMII_TX_DRIVER_400MV;
+ break;
+ case 500:
+ tx_drive_strength = PQSGMII_TX_DRIVER_500MV;
+ break;
+ case 600:
+ tx_drive_strength = PQSGMII_TX_DRIVER_600MV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Workaround to enable AZ transmitting ability */
+ psgmii_az = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PSGMII_MODE_CTRL);
+ psgmii_az &= ~PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK;
+ psgmii_az |= FIELD_PREP(PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK, 0xc);
+ ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, PSGMII_MODE_CTRL, psgmii_az);
+ psgmii_az = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PSGMII_MODE_CTRL);
+
+
+ tx_amp = phy_read(phydev, MDIO_DEVAD_NONE, PSGMII_QSGMII_DRIVE_CONTROL_1);
+ tx_amp &= ~PSGMII_QSGMII_TX_DRIVER_MASK;
+ tx_amp |= FIELD_PREP(PSGMII_QSGMII_TX_DRIVER_MASK, tx_drive_strength);
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, PSGMII_QSGMII_DRIVE_CONTROL_1, tx_amp);
+
+ return ret;
+}
+
+U_BOOT_PHY_DRIVER(QCA8072_driver) = {
+ .name = "Qualcomm QCA8072",
+ .uid = PHY_ID_QCA8072,
+ .mask = PHY_ID_QCA807X_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .config = qca807x_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+U_BOOT_PHY_DRIVER(QCA8075_driver) = {
+ .name = "Qualcomm QCA8075",
+ .uid = PHY_ID_QCA8075,
+ .mask = PHY_ID_QCA807X_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .config = qca807x_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+U_BOOT_PHY_DRIVER(QCA807X_PSGMII_driver) = {
+ .name = "Qualcomm QCA807x PSGMII",
+ .uid = PHY_ID_QCA807X_PSGMII,
+ .mask = PHY_ID_QCA807X_MASK,
+ .config = qca807x_psgmii_config,
+};
--
2.45.1
More information about the U-Boot
mailing list