[openwrt/openwrt] qualcommbe: v6.12: add QCA8084 ethernet PHY driver
LEDE Commits
lede-commits at lists.infradead.org
Sat May 31 03:25:58 PDT 2025
robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/390e516a3b872c07b5231483658b75efc594f2c9
commit 390e516a3b872c07b5231483658b75efc594f2c9
Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
AuthorDate: Tue May 13 21:38:58 2025 -0500
qualcommbe: v6.12: add QCA8084 ethernet PHY driver
This driver is cherry-picked from target/linux/qualcommbe/patches-6.6.
While Qualcomm did submit past patches for QCA8084, the code has since
ben split from at803x. The existing OpenWRT version of the patch is
the cleanest version I could find. Add it here.
Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
Link: https://github.com/openwrt/openwrt/pull/18796
Signed-off-by: Robert Marko <robimarko at gmail.com>
---
...-net-Document-Qualcomm-QCA8084-PHY-packag.patch | 244 +++++++++++++++++++++
...-qca808x-Add-QCA8084-ethernet-phy-support.patch | 133 +++++++++++
...808x-Add-config_init-function-for-QCA8084.patch | 85 +++++++
...808x-Add-link_change_notify-function-for-.patch | 90 ++++++++
...808x-Add-register-access-support-routines.patch | 125 +++++++++++
...et-phy-qca808x-Add-QCA8084-probe-function.patch | 140 ++++++++++++
...808x-Add-package-clocks-and-resets-for-QC.patch | 132 +++++++++++
...qca808x-Add-QCA8084-package-init-function.patch | 171 +++++++++++++++
8 files changed, 1120 insertions(+)
diff --git a/target/linux/qualcommbe/patches-6.12/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch b/target/linux/qualcommbe/patches-6.12/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch
new file mode 100644
index 0000000000..1583258493
--- /dev/null
+++ b/target/linux/qualcommbe/patches-6.12/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch
@@ -0,0 +1,244 @@
+From ae682f13d308682232069e5150e884fc10160598 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj at quicinc.com>
+Date: Mon, 29 Jan 2024 17:57:20 +0800
+Subject: [PATCH] dt-bindings: net: Document Qualcomm QCA8084 PHY package
+
+QCA8084 is quad PHY chip, which integrates 4 PHYs, 2 PCS
+interfaces (PCS0 and PCS1) and clock controller, which can
+also be integrated to the switch chip named as QCA8386.
+
+1. MDIO address of 4 PHYs, 2 PCS and 1 XPCS (PCS1 includes
+ PCS and XPCS, PCS0 includes PCS) can be configured.
+2. The package mode of PHY is optionally configured for the
+ interface mode of two PCSes working correctly.
+3. The package level clock and reset need to be initialized.
+4. The clock and reset per PHY device need to be initialized
+ so that the PHY register can be accessed.
+
+Change-Id: Idb2338d2673152cbd3c57e95968faa59e9d4a80f
+Signed-off-by: Luo Jie <quic_luoj at quicinc.com>
+---
+ .../devicetree/bindings/net/qcom,qca8084.yaml | 198 ++++++++++++++++++
+ include/dt-bindings/net/qcom,qca808x.h | 14 ++
+ 2 files changed, 212 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/qcom,qca8084.yaml
+ create mode 100644 include/dt-bindings/net/qcom,qca808x.h
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/qcom,qca8084.yaml
+@@ -0,0 +1,198 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/qcom,qca8084.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm QCA8084 Ethernet Quad PHY
++
++maintainers:
++ - Luo Jie <quic_luoj at quicinc.com>
++
++description:
++ Qualcomm QCA8084 is a four-port Ethernet transceiver, the
++ Ethernet port supports link speed 10/100/1000/2500 Mbps.
++ There are two PCSes (PCS0 and PCS1) integrated in the PHY
++ package, PCS1 includes XPCS and PCS to support the interface
++ mode 10G-QXGMII and SGMII, PCS0 includes a PCS to support the
++ interface mode SGMII only. There is also a clock controller
++ integrated in the PHY package. This four-port Ethernet
++ transceiver can also be integrated to the switch chip named
++ as QCA8386. The PHY package mode needs to be configured as the
++ correct value to apply the interface mode of two PCSes as
++ mentioned below.
++
++ QCA8084 expects an input reference clock 50 MHZ as the clock
++ source of the integrated clock controller, the integrated
++ clock controller supplies the clocks and resets to the
++ integrated PHY, PCS and PHY package.
++
++ - |
++ +--| |--+-------------------+--| |--+
++ | PCS1 |<------------+---->| PCS0 |
++ +-------+ | +-------+
++ | | |
++ Ref 50M clk +--------+ | |
++ ------------>| | clk & rst | |
++ GPIO Reset |QCA8K_CC+------------+ |
++ ------------>| | | |
++ +--------+ | |
++ | V |
++ +--------+--------+--------+--------+
++ | PHY0 | PHY1 | PHY2 | PHY3 |
++ +--------+--------+--------+--------+
++
++$ref: ethernet-phy-package.yaml#
++
++properties:
++ compatible:
++ const: qcom,qca8084-package
++
++ clocks:
++ description: PHY package level initial common clocks, which are
++ needed to be enabled after GPIO reset on the PHY package, these
++ clocks are supplied from the PHY integrated clock controller
++ (QCA8K-CC).
++ items:
++ - description: APB bridge clock
++ - description: AHB clock
++ - description: Security control clock
++ - description: TLMM clock
++ - description: TLMM AHB clock
++ - description: CNOC AHB clock
++ - description: MDIO AHB clock
++
++ clock-names:
++ items:
++ - const: apb_bridge
++ - const: ahb
++ - const: sec_ctrl_ahb
++ - const: tlmm
++ - const: tlmm_ahb
++ - const: cnoc_ahb
++ - const: mdio_ahb
++
++ resets:
++ description: PHY package level initial common reset, which are
++ needed to be deasserted after GPIO reset on the PHY package,
++ this reset is provided by the PHY integrated clock controller
++ to do PHY DSP reset.
++ maxItems: 1
++
++ qcom,package-mode:
++ description: |
++ The package mode of PHY supports to be configured as 3 modes
++ to apply the combinations of interface mode of two PCSes
++ correctly. This value should use one of the values defined in
++ dt-bindings/net/qcom,qca808x.h. The package mode 10G-QXGMII of
++ Quad PHY is used by default.
++
++ package mode PCS1 PCS0
++ phy mode (0) 10G-QXGMII for not used
++ PHY0-PHY3
++
++ switch mode (1) SGMII for SGMII for
++ switch MAC0 switch MAC5 (optional)
++
++ switch bypass MAC5 (2) SGMII for SGMII for
++ switch MAC0 PHY3
++ $ref: /schemas/types.yaml#/definitions/uint32
++ enum: [0, 1, 2]
++ default: 0
++
++ qcom,phy-addr-fixup:
++ description: MDIO address for PHY0-PHY3, PCS0 and PCS1 including
++ PCS and XPCS, which can be optionally customized by programming
++ the security control register of PHY package. The hardware default
++ MDIO address of PHY0-PHY3, PCS0 and PCS1 including PCS and XPCS is
++ 0-6.
++ $ref: /schemas/types.yaml#/definitions/uint32-array
++ minItems: 7
++ maxItems: 7
++
++patternProperties:
++ ^ethernet-phy(@[a-f0-9]+)?$:
++ $ref: ethernet-phy.yaml#
++
++ properties:
++ compatible:
++ const: ethernet-phy-id004d.d180
++
++ required:
++ - compatible
++ - reg
++ - clocks
++ - resets
++
++ unevaluatedProperties: false
++
++required:
++ - compatible
++ - clocks
++ - clock-names
++ - resets
++
++unevaluatedProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/clock/qcom,qca8k-nsscc.h>
++ #include <dt-bindings/net/qcom,qca808x.h>
++ #include <dt-bindings/reset/qcom,qca8k-nsscc.h>
++
++ mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ ethernet-phy-package at 1 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "qcom,qca8084-package";
++ reg = <1>;
++ clocks = <&qca8k_nsscc NSS_CC_APB_BRIDGE_CLK>,
++ <&qca8k_nsscc NSS_CC_AHB_CLK>,
++ <&qca8k_nsscc NSS_CC_SEC_CTRL_AHB_CLK>,
++ <&qca8k_nsscc NSS_CC_TLMM_CLK>,
++ <&qca8k_nsscc NSS_CC_TLMM_AHB_CLK>,
++ <&qca8k_nsscc NSS_CC_CNOC_AHB_CLK>,
++ <&qca8k_nsscc NSS_CC_MDIO_AHB_CLK>;
++ clock-names = "apb_bridge",
++ "ahb",
++ "sec_ctrl_ahb",
++ "tlmm",
++ "tlmm_ahb",
++ "cnoc_ahb",
++ "mdio_ahb";
++ resets = <&qca8k_nsscc NSS_CC_GEPHY_FULL_ARES>;
++ qcom,package-mode = <QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_MAC>;
++ qcom,phy-addr-fixup = <1 2 3 4 5 6 7>;
++
++ ethernet-phy at 1 {
++ compatible = "ethernet-phy-id004d.d180";
++ reg = <1>;
++ clocks = <&qca8k_nsscc NSS_CC_GEPHY0_SYS_CLK>;
++ resets = <&qca8k_nsscc NSS_CC_GEPHY0_SYS_ARES>;
++ };
++
++ ethernet-phy at 2 {
++ compatible = "ethernet-phy-id004d.d180";
++ reg = <2>;
++ clocks = <&qca8k_nsscc NSS_CC_GEPHY1_SYS_CLK>;
++ resets = <&qca8k_nsscc NSS_CC_GEPHY1_SYS_ARES>;
++ };
++
++ ethernet-phy at 3 {
++ compatible = "ethernet-phy-id004d.d180";
++ reg = <3>;
++ clocks = <&qca8k_nsscc NSS_CC_GEPHY2_SYS_CLK>;
++ resets = <&qca8k_nsscc NSS_CC_GEPHY2_SYS_ARES>;
++ };
++
++ ethernet-phy at 4 {
++ compatible = "ethernet-phy-id004d.d180";
++ reg = <4>;
++ clocks = <&qca8k_nsscc NSS_CC_GEPHY3_SYS_CLK>;
++ resets = <&qca8k_nsscc NSS_CC_GEPHY3_SYS_ARES>;
++ };
++ };
++ };
+--- /dev/null
++++ b/include/dt-bindings/net/qcom,qca808x.h
+@@ -0,0 +1,14 @@
++/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
++/*
++ * Device Tree constants for the Qualcomm QCA808X PHYs
++ */
++
++#ifndef _DT_BINDINGS_QCOM_QCA808X_H
++#define _DT_BINDINGS_QCOM_QCA808X_H
++
++/* PHY package modes of QCA8084 to apply the interface modes of two PCSes. */
++#define QCA808X_PCS1_10G_QXGMII_PCS0_UNUNSED 0
++#define QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_MAC 1
++#define QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_PHY 2
++
++#endif
diff --git a/target/linux/qualcommbe/patches-6.12/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch b/target/linux/qualcommbe/patches-6.12/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch
new file mode 100644
index 0000000000..edfe8d1404
--- /dev/null
+++ b/target/linux/qualcommbe/patches-6.12/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch
@@ -0,0 +1,133 @@
+From 816bff9bcd2ff7c1e84dd14fc81c9c1bdaa609e7 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj at quicinc.com>
+Date: Thu, 6 Apr 2023 18:09:07 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 ethernet phy support
+
+Add QCA8084 Quad-PHY support, which is a four-port PHY with
+maximum link capability of 2.5 Gbps. The features of each port
+are almost same as QCA8081. The slave seed and fast retrain
+configs are not needed for QCA8084. It includes two PCSes.
+
+PCS0 of QCA8084 supports the interface modes:
+PHY_INTERFACE_MODE_2500BASEX and PHY_INTERFACE_MODE_SGMII.
+
+PCS1 of QCA8084 supports the interface modes:
+PHY_INTERFACE_MODE_10G_QXGMII, PHY_INTERFACE_MODE_2500BASEX and
+PHY_INTERFACE_MODE_SGMII.
+
+The additional CDT configurations needed for QCA8084 compared
+with QCA8081.
+
+Change-Id: I12555fa70662682474ab4432204405b5e752fef6
+Signed-off-by: Luo Jie <quic_luoj at quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 62 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 60 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -86,9 +86,16 @@
+ #define QCA8081_PHY_FIFO_RSTN BIT(11)
+
+ #define QCA8081_PHY_ID 0x004dd101
++#define QCA8084_PHY_ID 0x004dd180
++
++#define QCA8084_MMD3_CDT_PULSE_CTRL 0x8075
++#define QCA8084_CDT_PULSE_THRESH_VAL 0xa060
++
++#define QCA8084_MMD3_CDT_NEAR_CTRL 0x807f
++#define QCA8084_CDT_NEAR_BYPASS BIT(15)
+
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+-MODULE_AUTHOR("Matus Ujhelyi");
++MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+
+ struct qca808x_priv {
+@@ -153,7 +160,9 @@ static bool qca808x_is_prefer_master(str
+
+ static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
+ {
+- return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
++ return phydev_id_compare(phydev, QCA8081_PHY_ID) &&
++ linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++ phydev->supported);
+ }
+
+ static bool qca808x_is_1g_only(struct phy_device *phydev)
+@@ -273,6 +282,23 @@ static int qca808x_read_status(struct ph
+ return ret;
+
+ if (phydev->link) {
++ /* There are two PCSes available for QCA8084, which support
++ * the following interface modes.
++ *
++ * 1. PHY_INTERFACE_MODE_10G_QXGMII utilizes PCS1 for all
++ * available 4 ports, which is for all link speeds.
++ *
++ * 2. PHY_INTERFACE_MODE_2500BASEX utilizes PCS0 for the
++ * fourth port, which is only for the link speed 2500M same
++ * as QCA8081.
++ *
++ * 3. PHY_INTERFACE_MODE_SGMII utilizes PCS0 for the fourth
++ * port, which is for the link speed 10M, 100M and 1000M same
++ * as QCA8081.
++ */
++ if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
++ return 0;
++
+ if (phydev->speed == SPEED_2500)
+ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+ else
+@@ -352,6 +378,18 @@ static int qca808x_cable_test_start(stru
+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
+
++ if (phydev_id_compare(phydev, QCA8084_PHY_ID)) {
++ /* Adjust the positive and negative pulse thereshold of CDT. */
++ phy_write_mmd(phydev, MDIO_MMD_PCS,
++ QCA8084_MMD3_CDT_PULSE_CTRL,
++ QCA8084_CDT_PULSE_THRESH_VAL);
++
++ /* Disable the near bypass of CDT. */
++ phy_modify_mmd(phydev, MDIO_MMD_PCS,
++ QCA8084_MMD3_CDT_NEAR_CTRL,
++ QCA8084_CDT_NEAR_BYPASS, 0);
++ }
++
+ return 0;
+ }
+
+@@ -651,12 +689,32 @@ static struct phy_driver qca808x_driver[
+ .led_hw_control_set = qca808x_led_hw_control_set,
+ .led_hw_control_get = qca808x_led_hw_control_get,
+ .led_polarity_set = qca808x_led_polarity_set,
++}, {
++ /* Qualcomm QCA8084 */
++ PHY_ID_MATCH_MODEL(QCA8084_PHY_ID),
++ .name = "Qualcomm QCA8084",
++ .flags = PHY_POLL_CABLE_TEST,
++ .config_intr = at803x_config_intr,
++ .handle_interrupt = at803x_handle_interrupt,
++ .get_tunable = at803x_get_tunable,
++ .set_tunable = at803x_set_tunable,
++ .set_wol = at803x_set_wol,
++ .get_wol = at803x_get_wol,
++ .get_features = qca808x_get_features,
++ .config_aneg = qca808x_config_aneg,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
++ .read_status = qca808x_read_status,
++ .soft_reset = qca808x_soft_reset,
++ .cable_test_start = qca808x_cable_test_start,
++ .cable_test_get_status = qca808x_cable_test_get_status,
+ }, };
+
+ module_phy_driver(qca808x_driver);
+
+ static const struct mdio_device_id __maybe_unused qca808x_tbl[] = {
+ { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
++ { PHY_ID_MATCH_MODEL(QCA8084_PHY_ID) },
+ { }
+ };
+
diff --git a/target/linux/qualcommbe/patches-6.12/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch b/target/linux/qualcommbe/patches-6.12/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch
new file mode 100644
index 0000000000..196160188b
--- /dev/null
+++ b/target/linux/qualcommbe/patches-6.12/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch
@@ -0,0 +1,85 @@
+From 5a57611512593212b7fd9c23b4d96486bab6dee3 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj at quicinc.com>
+Date: Wed, 8 Nov 2023 16:18:02 +0800
+Subject: [PATCH] net: phy: qca808x: Add config_init function for QCA8084
+
+1. The ADC of QCA8084 PHY must be configured as edge inverted
+and falling whenever it is initialized or reset. In addition,
+the default MSE (Mean square error) threshold value is adjusted,
+which comes into play during link partner detection to detect
+the valid link signal.
+
+2. Add the possible interface modes.
+ When QCA8084 works on the interface mode SGMII or 2500BASE-X, the
+ interface mode can be switched according to the PHY link speed.
+
+ When QCA8084 works on the 10G-QXGMII mode, which will be the only
+ possible interface mode.
+
+Change-Id: I832c0d0b069e95cc411a8a7b680a5f60e1d6041a
+Signed-off-by: Luo Jie <quic_luoj at quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 38 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -94,6 +94,15 @@
+ #define QCA8084_MMD3_CDT_NEAR_CTRL 0x807f
+ #define QCA8084_CDT_NEAR_BYPASS BIT(15)
+
++/* QCA8084 ADC clock edge */
++#define QCA8084_ADC_CLK_SEL 0x8b80
++#define QCA8084_ADC_CLK_SEL_ACLK GENMASK(7, 4)
++#define QCA8084_ADC_CLK_SEL_ACLK_FALL 0xf
++#define QCA8084_ADC_CLK_SEL_ACLK_RISE 0x0
++
++#define QCA8084_MSE_THRESHOLD 0x800a
++#define QCA8084_MSE_THRESHOLD_2P5G_VAL 0x51c6
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -660,6 +669,34 @@ static int qca808x_led_polarity_set(stru
+ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
+ }
+
++static int qca8084_config_init(struct phy_device *phydev)
++{
++ int ret;
++
++ if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
++ __set_bit(PHY_INTERFACE_MODE_10G_QXGMII,
++ phydev->possible_interfaces);
++ else
++ qca808x_fill_possible_interfaces(phydev);
++
++ /* Configure the ADC to convert the signal using falling edge
++ * instead of the default rising edge.
++ */
++ ret = at803x_debug_reg_mask(phydev, QCA8084_ADC_CLK_SEL,
++ QCA8084_ADC_CLK_SEL_ACLK,
++ FIELD_PREP(QCA8084_ADC_CLK_SEL_ACLK,
++ QCA8084_ADC_CLK_SEL_ACLK_FALL));
++ if (ret < 0)
++ return ret;
++
++ /* Adjust MSE threshold value to avoid link issue with
++ * some link partner.
++ */
++ return phy_write_mmd(phydev, MDIO_MMD_PMAPMD,
++ QCA8084_MSE_THRESHOLD,
++ QCA8084_MSE_THRESHOLD_2P5G_VAL);
++}
++
+ static struct phy_driver qca808x_driver[] = {
+ {
+ /* Qualcomm QCA8081 */
+@@ -708,6 +745,7 @@ static struct phy_driver qca808x_driver[
+ .soft_reset = qca808x_soft_reset,
+ .cable_test_start = qca808x_cable_test_start,
+ .cable_test_get_status = qca808x_cable_test_get_status,
++ .config_init = qca8084_config_init,
+ }, };
+
+ module_phy_driver(qca808x_driver);
diff --git a/target/linux/qualcommbe/patches-6.12/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch b/target/linux/qualcommbe/patches-6.12/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch
new file mode 100644
index 0000000000..429b5c0535
--- /dev/null
+++ b/target/linux/qualcommbe/patches-6.12/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch
@@ -0,0 +1,90 @@
+From d1f2a1810af1833196934977f57607432fda46b4 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj at quicinc.com>
+Date: Wed, 8 Nov 2023 18:01:14 +0800
+Subject: [PATCH] net: phy: qca808x: Add link_change_notify function for
+ QCA8084
+
+When the link is changed, QCA8084 needs to do the fifo reset and
+adjust the IPG level for the 10G-QXGMII link on the speed 1000M.
+
+Change-Id: I21de802c78496fb95f1c5119fe3894c9fdebbd65
+Signed-off-by: Luo Jie <quic_luoj at quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 52 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 52 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -103,6 +103,14 @@
+ #define QCA8084_MSE_THRESHOLD 0x800a
+ #define QCA8084_MSE_THRESHOLD_2P5G_VAL 0x51c6
+
++/* QCA8084 FIFO reset control */
++#define QCA8084_FIFO_CONTROL 0x19
++#define QCA8084_FIFO_MAC_2_PHY BIT(1)
++#define QCA8084_FIFO_PHY_2_MAC BIT(0)
++
++#define QCA8084_MMD7_IPG_OP 0x901d
++#define QCA8084_IPG_10_TO_11_EN BIT(0)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -697,6 +705,49 @@ static int qca8084_config_init(struct ph
+ QCA8084_MSE_THRESHOLD_2P5G_VAL);
+ }
+
++static void qca8084_link_change_notify(struct phy_device *phydev)
++{
++ int ret;
++
++ /* Assert the FIFO between PHY and MAC. */
++ ret = phy_modify(phydev, QCA8084_FIFO_CONTROL,
++ QCA8084_FIFO_MAC_2_PHY | QCA8084_FIFO_PHY_2_MAC,
++ 0);
++ if (ret) {
++ phydev_err(phydev, "Asserting PHY FIFO failed\n");
++ return;
++ }
++
++ /* If the PHY is in 10G_QXGMII mode, the FIFO needs to be kept in
++ * reset state when link is down, otherwise the FIFO needs to be
++ * de-asserted after waiting 50 ms to make the assert completed.
++ */
++ if (phydev->interface != PHY_INTERFACE_MODE_10G_QXGMII ||
++ phydev->link) {
++ msleep(50);
++
++ /* Deassert the FIFO between PHY and MAC. */
++ ret = phy_modify(phydev, QCA8084_FIFO_CONTROL,
++ QCA8084_FIFO_MAC_2_PHY |
++ QCA8084_FIFO_PHY_2_MAC,
++ QCA8084_FIFO_MAC_2_PHY |
++ QCA8084_FIFO_PHY_2_MAC);
++ if (ret) {
++ phydev_err(phydev, "De-asserting PHY FIFO failed\n");
++ return;
++ }
++ }
++
++ /* Enable IPG level 10 to 11 tuning for link speed 1000M in the
++ * 10G_QXGMII mode.
++ */
++ if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
++ phy_modify_mmd(phydev, MDIO_MMD_AN, QCA8084_MMD7_IPG_OP,
++ QCA8084_IPG_10_TO_11_EN,
++ phydev->speed == SPEED_1000 ?
++ QCA8084_IPG_10_TO_11_EN : 0);
++}
++
+ static struct phy_driver qca808x_driver[] = {
+ {
+ /* Qualcomm QCA8081 */
+@@ -746,6 +797,7 @@ static struct phy_driver qca808x_driver[
+ .cable_test_start = qca808x_cable_test_start,
+ .cable_test_get_status = qca808x_cable_test_get_status,
+ .config_init = qca8084_config_init,
++ .link_change_notify = qca8084_link_change_notify,
+ }, };
+
+ module_phy_driver(qca808x_driver);
diff --git a/target/linux/qualcommbe/patches-6.12/0310-net-phy-qca808x-Add-register-access-support-routines.patch b/target/linux/qualcommbe/patches-6.12/0310-net-phy-qca808x-Add-register-access-support-routines.patch
new file mode 100644
index 0000000000..899352a116
--- /dev/null
+++ b/target/linux/qualcommbe/patches-6.12/0310-net-phy-qca808x-Add-register-access-support-routines.patch
@@ -0,0 +1,125 @@
+From c17f19be3bec0bf5467f4e14a21573836910f671 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj at quicinc.com>
+Date: Wed, 29 Nov 2023 15:21:22 +0800
+Subject: [PATCH] net: phy: qca808x: Add register access support routines for
+ QCA8084
+
+QCA8084 integrates clock controller and security control modules
+besides of the PHY and PCS. The 32bit registers in these modules
+are accessed using special MDIO sequences to read or write these
+registers.
+
+The MDIO address of PHY and PCS are configured by writing to the
+security control register. The package mode for QCA8084 is also
+configured in a similar manner.
+
+Change-Id: I9317307ef9bbc738a6adcbc3ea1be8e6528d711e
+Signed-off-by: Luo Jie <quic_luoj at quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 88 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 88 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -111,6 +111,22 @@
+ #define QCA8084_MMD7_IPG_OP 0x901d
+ #define QCA8084_IPG_10_TO_11_EN BIT(0)
+
++/* QCA8084 includes secure control module, which supports customizing the
++ * MDIO address of PHY device and PCS device and configuring package mode
++ * for the interface mode of PCS. The register of secure control is accessed
++ * by MDIO bus with the special MDIO sequences, where the 32 bits register
++ * address is split into 3 MDIO operations with 16 bits address.
++ */
++#define QCA8084_HIGH_ADDR_PREFIX 0x18
++#define QCA8084_LOW_ADDR_PREFIX 0x10
++
++/* Bottom two bits of REG must be zero */
++#define QCA8084_MII_REG_MASK GENMASK(4, 0)
++#define QCA8084_MII_PHY_ADDR_MASK GENMASK(7, 5)
++#define QCA8084_MII_PAGE_MASK GENMASK(23, 8)
++#define QCA8084_MII_SW_ADDR_MASK GENMASK(31, 24)
++#define QCA8084_MII_REG_DATA_UPPER_16_BITS BIT(1)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -119,6 +135,78 @@ struct qca808x_priv {
+ int led_polarity_mode;
+ };
+
++static int __qca8084_set_page(struct mii_bus *bus, u16 sw_addr, u16 page)
++{
++ return __mdiobus_write(bus, QCA8084_HIGH_ADDR_PREFIX | (sw_addr >> 5),
++ sw_addr & 0x1f, page);
++}
++
++static int __qca8084_mii_read(struct mii_bus *bus, u16 addr, u16 reg, u32 *val)
++{
++ int ret, data;
++
++ ret = __mdiobus_read(bus, addr, reg);
++ if (ret < 0)
++ return ret;
++
++ data = ret;
++ ret = __mdiobus_read(bus, addr,
++ reg | QCA8084_MII_REG_DATA_UPPER_16_BITS);
++ if (ret < 0)
++ return ret;
++
++ *val = data | ret << 16;
++
++ return 0;
++}
++
++static int __qca8084_mii_write(struct mii_bus *bus, u16 addr, u16 reg, u32 val)
++{
++ int ret;
++
++ ret = __mdiobus_write(bus, addr, reg, lower_16_bits(val));
++ if (!ret)
++ ret = __mdiobus_write(bus, addr,
++ reg | QCA8084_MII_REG_DATA_UPPER_16_BITS,
++ upper_16_bits(val));
++
++ return ret;
++}
++
++static int qca8084_mii_modify(struct phy_device *phydev, u32 regaddr,
++ u32 clear, u32 set)
++{
++ u16 reg, addr, page, sw_addr;
++ struct mii_bus *bus;
++ u32 val;
++ int ret;
++
++ bus = phydev->mdio.bus;
++ mutex_lock(&bus->mdio_lock);
++
++ reg = FIELD_GET(QCA8084_MII_REG_MASK, regaddr);
++ addr = FIELD_GET(QCA8084_MII_PHY_ADDR_MASK, regaddr);
++ page = FIELD_GET(QCA8084_MII_PAGE_MASK, regaddr);
++ sw_addr = FIELD_GET(QCA8084_MII_SW_ADDR_MASK, regaddr);
++
++ ret = __qca8084_set_page(bus, sw_addr, page);
++ if (ret < 0)
++ goto qca8084_mii_modify_exit;
++
++ ret = __qca8084_mii_read(bus, QCA8084_LOW_ADDR_PREFIX | addr,
++ reg, &val);
++ if (ret < 0)
++ goto qca8084_mii_modify_exit;
++
++ val &= ~clear;
++ val |= set;
++ ret = __qca8084_mii_write(bus, QCA8084_LOW_ADDR_PREFIX | addr,
++ reg, val);
++qca8084_mii_modify_exit:
++ mutex_unlock(&bus->mdio_lock);
++ return ret;
++};
++
+ static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
+ {
+ int ret;
diff --git a/target/linux/qualcommbe/patches-6.12/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch b/target/linux/qualcommbe/patches-6.12/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch
new file mode 100644
index 0000000000..b8c5e9ee91
--- /dev/null
+++ b/target/linux/qualcommbe/patches-6.12/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch
@@ -0,0 +1,140 @@
+From 485f973c5b1d889bd1f48a188137d80d45004991 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj at quicinc.com>
+Date: Mon, 29 Jan 2024 10:51:38 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 probe function
+
+Add the PHY package probe function. The MDIO slave address of
+PHY, PCS and XPCS can be optionally customized by configuring
+the PHY package level register.
+
+In addition, enable system clock of PHY and de-assert PHY in
+the probe function so that the register of PHY device can be
+accessed, and the features of PHY can be acquired.
+
+Change-Id: I2251b9c5c398a21a4ef547a727189a934ad3a44c
+Signed-off-by: Luo Jie <quic_luoj at quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 91 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 91 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -2,6 +2,8 @@
+
+ #include <linux/phy.h>
+ #include <linux/module.h>
++#include <linux/of.h>
++#include <linux/clk.h>
+
+ #include "qcom.h"
+
+@@ -127,6 +129,21 @@
+ #define QCA8084_MII_SW_ADDR_MASK GENMASK(31, 24)
+ #define QCA8084_MII_REG_DATA_UPPER_16_BITS BIT(1)
+
++/* QCA8084 integrates 4 PHYs, PCS0 and PCS1(includes PCS and XPCS). */
++#define QCA8084_MDIO_DEVICE_NUM 7
++
++#define QCA8084_PCS_CFG 0xc90f014
++#define QCA8084_PCS_ADDR0_MASK GENMASK(4, 0)
++#define QCA8084_PCS_ADDR1_MASK GENMASK(9, 5)
++#define QCA8084_PCS_ADDR2_MASK GENMASK(14, 10)
++
++#define QCA8084_EPHY_CFG 0xc90f018
++#define QCA8084_EPHY_ADDR0_MASK GENMASK(4, 0)
++#define QCA8084_EPHY_ADDR1_MASK GENMASK(9, 5)
++#define QCA8084_EPHY_ADDR2_MASK GENMASK(14, 10)
++#define QCA8084_EPHY_ADDR3_MASK GENMASK(19, 15)
++#define QCA8084_EPHY_LDO_EN GENMASK(21, 20)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -836,6 +853,79 @@ static void qca8084_link_change_notify(s
+ QCA8084_IPG_10_TO_11_EN : 0);
+ }
+
++static int qca8084_phy_package_probe_once(struct phy_device *phydev)
++{
++ int addr[QCA8084_MDIO_DEVICE_NUM] = {0, 1, 2, 3, 4, 5, 6};
++ struct phy_package_shared *shared = phydev->shared;
++ int ret, clear, set;
++
++ /* Program the MDIO address of PHY and PCS optionally, the MDIO
++ * address 0-6 is used for PHY and PCS MDIO devices by default.
++ */
++ ret = of_property_read_u32_array(shared->np,
++ "qcom,phy-addr-fixup",
++ addr, ARRAY_SIZE(addr));
++ if (ret && ret != -EINVAL)
++ return ret;
++
++ /* Configure the MDIO addresses for the four PHY devices. */
++ clear = QCA8084_EPHY_ADDR0_MASK | QCA8084_EPHY_ADDR1_MASK |
++ QCA8084_EPHY_ADDR2_MASK | QCA8084_EPHY_ADDR3_MASK;
++ set = FIELD_PREP(QCA8084_EPHY_ADDR0_MASK, addr[0]);
++ set |= FIELD_PREP(QCA8084_EPHY_ADDR1_MASK, addr[1]);
++ set |= FIELD_PREP(QCA8084_EPHY_ADDR2_MASK, addr[2]);
++ set |= FIELD_PREP(QCA8084_EPHY_ADDR3_MASK, addr[3]);
++
++ ret = qca8084_mii_modify(phydev, QCA8084_EPHY_CFG, clear, set);
++ if (ret)
++ return ret;
++
++ /* Configure the MDIO addresses for PCS0 and PCS1 including
++ * PCS and XPCS.
++ */
++ clear = QCA8084_PCS_ADDR0_MASK | QCA8084_PCS_ADDR1_MASK |
++ QCA8084_PCS_ADDR2_MASK;
++ set = FIELD_PREP(QCA8084_PCS_ADDR0_MASK, addr[4]);
++ set |= FIELD_PREP(QCA8084_PCS_ADDR1_MASK, addr[5]);
++ set |= FIELD_PREP(QCA8084_PCS_ADDR2_MASK, addr[6]);
++
++ return qca8084_mii_modify(phydev, QCA8084_PCS_CFG, clear, set);
++}
++
++static int qca8084_probe(struct phy_device *phydev)
++{
++ struct device *dev = &phydev->mdio.dev;
++ struct reset_control *rstc;
++ struct clk *clk;
++ int ret;
++
++ ret = devm_of_phy_package_join(dev, phydev, 0);
++ if (ret)
++ return ret;
++
++ if (phy_package_probe_once(phydev)) {
++ ret = qca8084_phy_package_probe_once(phydev);
++ if (ret)
++ return ret;
++ }
++
++ /* Enable clock of PHY device, so that the PHY register
++ * can be accessed to get PHY features.
++ */
++ clk = devm_clk_get_enabled(dev, NULL);
++ if (IS_ERR(clk))
++ return dev_err_probe(dev, PTR_ERR(clk),
++ "Enable PHY clock failed\n");
++
++ /* De-assert PHY reset after the clock of PHY enabled. */
++ rstc = devm_reset_control_get_exclusive(dev, NULL);
++ if (IS_ERR(rstc))
++ return dev_err_probe(dev, PTR_ERR(rstc),
++ "Get PHY reset failed\n");
++
++ return reset_control_deassert(rstc);
++}
++
+ static struct phy_driver qca808x_driver[] = {
+ {
+ /* Qualcomm QCA8081 */
+@@ -886,6 +976,7 @@ static struct phy_driver qca808x_driver[
+ .cable_test_get_status = qca808x_cable_test_get_status,
+ .config_init = qca8084_config_init,
+ .link_change_notify = qca8084_link_change_notify,
++ .probe = qca8084_probe,
+ }, };
+
+ module_phy_driver(qca808x_driver);
diff --git a/target/linux/qualcommbe/patches-6.12/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch b/target/linux/qualcommbe/patches-6.12/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch
new file mode 100644
index 0000000000..b41e725d30
--- /dev/null
+++ b/target/linux/qualcommbe/patches-6.12/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch
@@ -0,0 +1,132 @@
+From 685566f8b765f522b7f4d4deb06bf84a557dc4ac Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj at quicinc.com>
+Date: Tue, 9 Apr 2024 16:30:55 +0800
+Subject: [PATCH] net: phy: qca808x: Add package clocks and resets for QCA8084
+
+Parse the PHY package clocks from the PHY package DTS node.
+These package level clocks will be enabled in the PHY package
+init function.
+
+Deassert PHY package reset, which is necessary for accessing
+the PHY registers.
+
+Change-Id: I254d0aa0a1155d3618c6f1fc7d7a5b6ecadccbaa
+Signed-off-by: Luo Jie <quic_luoj at quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 67 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 64 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -4,6 +4,7 @@
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/clk.h>
++#include <linux/reset.h>
+
+ #include "qcom.h"
+
+@@ -148,10 +149,35 @@ MODULE_DESCRIPTION("Qualcomm Atheros QCA
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+
++enum {
++ APB_BRIDGE_CLK,
++ AHB_CLK,
++ SEC_CTRL_AHB_CLK,
++ TLMM_CLK,
++ TLMM_AHB_CLK,
++ CNOC_AHB_CLK,
++ MDIO_AHB_CLK,
++ PACKAGE_CLK_MAX
++};
++
+ struct qca808x_priv {
+ int led_polarity_mode;
+ };
+
++struct qca808x_shared_priv {
++ struct clk *clk[PACKAGE_CLK_MAX];
++};
++
++static const char *const qca8084_package_clk_name[PACKAGE_CLK_MAX] = {
++ [APB_BRIDGE_CLK] = "apb_bridge",
++ [AHB_CLK] = "ahb",
++ [SEC_CTRL_AHB_CLK] = "sec_ctrl_ahb",
++ [TLMM_CLK] = "tlmm",
++ [TLMM_AHB_CLK] = "tlmm_ahb",
++ [CNOC_AHB_CLK] = "cnoc_ahb",
++ [MDIO_AHB_CLK] = "mdio_ahb",
++};
++
+ static int __qca8084_set_page(struct mii_bus *bus, u16 sw_addr, u16 page)
+ {
+ return __mdiobus_write(bus, QCA8084_HIGH_ADDR_PREFIX | (sw_addr >> 5),
+@@ -853,11 +879,24 @@ static void qca8084_link_change_notify(s
+ QCA8084_IPG_10_TO_11_EN : 0);
+ }
+
++/* QCA8084 is a four-port PHY, which integrates the clock controller,
++ * 4 PHY devices and 2 PCS interfaces (PCS0 and PCS1). PCS1 includes
++ * XPCS and PCS to support 10G-QXGMII and SGMII. PCS0 includes one PCS
++ * to support SGMII.
++ *
++ * The clocks and resets are sourced from the integrated clock controller
++ * of the PHY package. This integrated clock controller is driven by a
++ * QCA8K clock provider that supplies the clocks and resets to the four
++ * PHYs, PCS and PHY package.
++ */
+ static int qca8084_phy_package_probe_once(struct phy_device *phydev)
+ {
+ int addr[QCA8084_MDIO_DEVICE_NUM] = {0, 1, 2, 3, 4, 5, 6};
+ struct phy_package_shared *shared = phydev->shared;
+- int ret, clear, set;
++ struct qca808x_shared_priv *shared_priv;
++ struct reset_control *rstc;
++ int i, ret, clear, set;
++ struct clk *clk;
+
+ /* Program the MDIO address of PHY and PCS optionally, the MDIO
+ * address 0-6 is used for PHY and PCS MDIO devices by default.
+@@ -889,17 +928,39 @@ static int qca8084_phy_package_probe_onc
+ set |= FIELD_PREP(QCA8084_PCS_ADDR1_MASK, addr[5]);
+ set |= FIELD_PREP(QCA8084_PCS_ADDR2_MASK, addr[6]);
+
+- return qca8084_mii_modify(phydev, QCA8084_PCS_CFG, clear, set);
++ ret = qca8084_mii_modify(phydev, QCA8084_PCS_CFG, clear, set);
++ if (ret)
++ return ret;
++
++ shared_priv = shared->priv;
++ for (i = 0; i < ARRAY_SIZE(qca8084_package_clk_name); i++) {
++ clk = of_clk_get_by_name(shared->np,
++ qca8084_package_clk_name[i]);
++ if (IS_ERR(clk))
++ return dev_err_probe(&phydev->mdio.dev, PTR_ERR(clk),
++ "package clock %s not ready\n",
++ qca8084_package_clk_name[i]);
++ shared_priv->clk[i] = clk;
++ }
++
++ rstc = of_reset_control_get_exclusive(shared->np, NULL);
++ if (IS_ERR(rstc))
++ return dev_err_probe(&phydev->mdio.dev, PTR_ERR(rstc),
++ "package reset not ready\n");
++
++ /* Deassert PHY package. */
++ return reset_control_deassert(rstc);
+ }
+
+ static int qca8084_probe(struct phy_device *phydev)
+ {
++ struct qca808x_shared_priv *shared_priv;
+ struct device *dev = &phydev->mdio.dev;
+ struct reset_control *rstc;
+ struct clk *clk;
+ int ret;
+
+- ret = devm_of_phy_package_join(dev, phydev, 0);
++ ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
+ if (ret)
+ return ret;
+
diff --git a/target/linux/qualcommbe/patches-6.12/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch b/target/linux/qualcommbe/patches-6.12/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch
new file mode 100644
index 0000000000..bb5d0728e4
--- /dev/null
+++ b/target/linux/qualcommbe/patches-6.12/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch
@@ -0,0 +1,171 @@
+From bf779b10b00fd79267d0ef625ae246df59ee23bd Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj at quicinc.com>
+Date: Thu, 25 Jan 2024 17:13:24 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 package init function
+
+The package mode of PHY is configured for the interface mode of two
+PCSes working correctly.
+
+The PHY package level clocks are enabled and their rates configured.
+
+Change-Id: I63d4b22d2a70ee713cc6a6818b0f3c7aa098a5f5
+Signed-off-by: Luo Jie <quic_luoj at quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 115 +++++++++++++++++++++++++++++++++
+ 1 file changed, 115 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -1,5 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0+
+
++#include <dt-bindings/net/qcom,qca808x.h>
+ #include <linux/phy.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+@@ -145,6 +146,13 @@
+ #define QCA8084_EPHY_ADDR3_MASK GENMASK(19, 15)
+ #define QCA8084_EPHY_LDO_EN GENMASK(21, 20)
+
++#define QCA8084_WORK_MODE_CFG 0xc90f030
++#define QCA8084_WORK_MODE_MASK GENMASK(5, 0)
++#define QCA8084_WORK_MODE_QXGMII (BIT(5) | GENMASK(3, 0))
++#define QCA8084_WORK_MODE_QXGMII_PORT4_SGMII (BIT(5) | GENMASK(2, 0))
++#define QCA8084_WORK_MODE_SWITCH BIT(4)
++#define QCA8084_WORK_MODE_SWITCH_PORT4_SGMII BIT(5)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -165,6 +173,7 @@ struct qca808x_priv {
+ };
+
+ struct qca808x_shared_priv {
++ int package_mode;
+ struct clk *clk[PACKAGE_CLK_MAX];
+ };
+
+@@ -808,10 +817,107 @@ static int qca808x_led_polarity_set(stru
+ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
+ }
+
++static int qca8084_package_clock_init(struct qca808x_shared_priv *shared_priv)
++{
++ int ret;
++
++ /* Configure clock rate 312.5MHZ for the PHY package
++ * APB bridge clock tree.
++ */
++ ret = clk_set_rate(shared_priv->clk[APB_BRIDGE_CLK], 312500000);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(shared_priv->clk[APB_BRIDGE_CLK]);
++ if (ret)
++ return ret;
++
++ /* Configure clock rate 104.17MHZ for the PHY package
++ * AHB clock tree.
++ */
++ ret = clk_set_rate(shared_priv->clk[AHB_CLK], 104170000);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(shared_priv->clk[AHB_CLK]);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(shared_priv->clk[SEC_CTRL_AHB_CLK]);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(shared_priv->clk[TLMM_CLK]);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(shared_priv->clk[TLMM_AHB_CLK]);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(shared_priv->clk[CNOC_AHB_CLK]);
++ if (ret)
++ return ret;
++
++ return clk_prepare_enable(shared_priv->clk[MDIO_AHB_CLK]);
++}
++
++static int qca8084_phy_package_config_init_once(struct phy_device *phydev)
++{
++ struct phy_package_shared *shared = phydev->shared;
++ struct qca808x_shared_priv *shared_priv;
++ int ret, mode;
++
++ shared_priv = shared->priv;
++ switch (shared_priv->package_mode) {
++ case QCA808X_PCS1_10G_QXGMII_PCS0_UNUNSED:
++ mode = QCA8084_WORK_MODE_QXGMII;
++ break;
++ case QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_MAC:
++ mode = QCA8084_WORK_MODE_SWITCH;
++ break;
++ case QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_PHY:
++ mode = QCA8084_WORK_MODE_SWITCH_PORT4_SGMII;
++ break;
++ default:
++ phydev_err(phydev, "Invalid qcom,package-mode %d\n",
++ shared_priv->package_mode);
++ return -EINVAL;
++ }
++
++ ret = qca8084_mii_modify(phydev, QCA8084_WORK_MODE_CFG,
++ QCA8084_WORK_MODE_MASK,
++ FIELD_PREP(QCA8084_WORK_MODE_MASK, mode));
++ if (ret)
++ return ret;
++
++ /* Initialize the PHY package clock and reset, which is the
++ * necessary config sequence after GPIO reset on the PHY package.
++ */
++ ret = qca8084_package_clock_init(shared_priv);
++ if (ret)
++ return ret;
++
++ /* Enable efuse loading into analog circuit */
++ ret = qca8084_mii_modify(phydev, QCA8084_EPHY_CFG,
++ QCA8084_EPHY_LDO_EN, 0);
++ if (ret)
++ return ret;
++
++ usleep_range(10000, 11000);
++ return ret;
++}
++
+ static int qca8084_config_init(struct phy_device *phydev)
+ {
+ int ret;
+
++ if (phy_package_init_once(phydev)) {
++ ret = qca8084_phy_package_config_init_once(phydev);
++ if (ret)
++ return ret;
++ }
++
+ if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
+ __set_bit(PHY_INTERFACE_MODE_10G_QXGMII,
+ phydev->possible_interfaces);
+@@ -948,6 +1054,15 @@ static int qca8084_phy_package_probe_onc
+ return dev_err_probe(&phydev->mdio.dev, PTR_ERR(rstc),
+ "package reset not ready\n");
+
++ /* The package mode 10G-QXGMII of PCS1 is used for Quad PHY and
++ * PCS0 is unused by default.
++ */
++ shared_priv->package_mode = QCA808X_PCS1_10G_QXGMII_PCS0_UNUNSED;
++ ret = of_property_read_u32(shared->np, "qcom,package-mode",
++ &shared_priv->package_mode);
++ if (ret && ret != -EINVAL)
++ return ret;
++
+ /* Deassert PHY package. */
+ return reset_control_deassert(rstc);
+ }
More information about the lede-commits
mailing list