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@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/18796
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Alexandru Gagniuc 2025-05-13 21:38:58 -05:00 committed by Robert Marko
parent 8f4100ac3b
commit 390e516a3b
8 changed files with 1120 additions and 0 deletions

View file

@ -0,0 +1,244 @@
From ae682f13d308682232069e5150e884fc10160598 Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@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@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@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@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@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@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@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@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

View file

@ -0,0 +1,133 @@
From 816bff9bcd2ff7c1e84dd14fc81c9c1bdaa609e7 Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@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@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) },
{ }
};

View file

@ -0,0 +1,85 @@
From 5a57611512593212b7fd9c23b4d96486bab6dee3 Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@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@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);

View file

@ -0,0 +1,90 @@
From d1f2a1810af1833196934977f57607432fda46b4 Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@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@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);

View file

@ -0,0 +1,125 @@
From c17f19be3bec0bf5467f4e14a21573836910f671 Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@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@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;

View file

@ -0,0 +1,140 @@
From 485f973c5b1d889bd1f48a188137d80d45004991 Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@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@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);

View file

@ -0,0 +1,132 @@
From 685566f8b765f522b7f4d4deb06bf84a557dc4ac Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@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@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;

View file

@ -0,0 +1,171 @@
From bf779b10b00fd79267d0ef625ae246df59ee23bd Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@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@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);
}