--- /dev/null
+From 986ec7ee75f2f1f7f93eb0e05f61f297f6123fce Mon Sep 17 00:00:00 2001
+Date: Mon, 3 Mar 2025 21:14:02 +0100
+Subject: [PATCH] v6.15: net: phy: move PHY package code from phy_device.c to
+ own source file
+
+This patch is the first step in moving the PHY package related code
+to its own source file. No functional change intended.
+
+---
+ drivers/net/phy/Makefile | 3 +-
+ drivers/net/phy/phy_device.c | 237 ---------------------------------
+ drivers/net/phy/phy_package.c | 244 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 246 insertions(+), 238 deletions(-)
+ create mode 100644 drivers/net/phy/phy_package.c
+
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -2,7 +2,8 @@
+ # Makefile for Linux PHY drivers
+
+ libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \
+- linkmode.o phy_link_topology.o
++ linkmode.o phy_link_topology.o \
++ phy_package.o
+ mdio-bus-y += mdio_bus.o mdio_device.o
+
+ ifdef CONFIG_MDIO_DEVICE
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1783,243 +1783,6 @@ bool phy_driver_is_genphy_10g(struct phy
+ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
+
+ /**
+- * phy_package_join - join a common PHY group
+- * @phydev: target phy_device struct
+- * @base_addr: cookie and base PHY address of PHY package for offset
+- * calculation of global register access
+- * @priv_size: if non-zero allocate this amount of bytes for private data
+- *
+- * This joins a PHY group and provides a shared storage for all phydevs in
+- * this group. This is intended to be used for packages which contain
+- * more than one PHY, for example a quad PHY transceiver.
+- *
+- * The base_addr parameter serves as cookie which has to have the same values
+- * for all members of one group and as the base PHY address of the PHY package
+- * for offset calculation to access generic registers of a PHY package.
+- * Usually, one of the PHY addresses of the different PHYs in the package
+- * provides access to these global registers.
+- * The address which is given here, will be used in the phy_package_read()
+- * and phy_package_write() convenience functions as base and added to the
+- * passed offset in those functions.
+- *
+- * This will set the shared pointer of the phydev to the shared storage.
+- * If this is the first call for a this cookie the shared storage will be
+- * allocated. If priv_size is non-zero, the given amount of bytes are
+- * allocated for the priv member.
+- *
+- * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
+- * with the same cookie but a different priv_size is an error.
+- */
+-int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size)
+-{
+- struct mii_bus *bus = phydev->mdio.bus;
+- struct phy_package_shared *shared;
+- int ret;
+-
+- if (base_addr < 0 || base_addr >= PHY_MAX_ADDR)
+- return -EINVAL;
+-
+- mutex_lock(&bus->shared_lock);
+- shared = bus->shared[base_addr];
+- if (!shared) {
+- ret = -ENOMEM;
+- shared = kzalloc(sizeof(*shared), GFP_KERNEL);
+- if (!shared)
+- goto err_unlock;
+- if (priv_size) {
+- shared->priv = kzalloc(priv_size, GFP_KERNEL);
+- if (!shared->priv)
+- goto err_free;
+- shared->priv_size = priv_size;
+- }
+- shared->base_addr = base_addr;
+- shared->np = NULL;
+- refcount_set(&shared->refcnt, 1);
+- bus->shared[base_addr] = shared;
+- } else {
+- ret = -EINVAL;
+- if (priv_size && priv_size != shared->priv_size)
+- goto err_unlock;
+- refcount_inc(&shared->refcnt);
+- }
+- mutex_unlock(&bus->shared_lock);
+-
+- phydev->shared = shared;
+-
+- return 0;
+-
+-err_free:
+- kfree(shared);
+-err_unlock:
+- mutex_unlock(&bus->shared_lock);
+- return ret;
+-}
+-EXPORT_SYMBOL_GPL(phy_package_join);
+-
+-/**
+- * of_phy_package_join - join a common PHY group in PHY package
+- * @phydev: target phy_device struct
+- * @priv_size: if non-zero allocate this amount of bytes for private data
+- *
+- * This is a variant of phy_package_join for PHY package defined in DT.
+- *
+- * The parent node of the @phydev is checked as a valid PHY package node
+- * structure (by matching the node name "ethernet-phy-package") and the
+- * base_addr for the PHY package is passed to phy_package_join.
+- *
+- * With this configuration the shared struct will also have the np value
+- * filled to use additional DT defined properties in PHY specific
+- * probe_once and config_init_once PHY package OPs.
+- *
+- * Returns < 0 on error, 0 on success. Esp. calling phy_package_join()
+- * with the same cookie but a different priv_size is an error. Or a parent
+- * node is not detected or is not valid or doesn't match the expected node
+- * name for PHY package.
+- */
+-int of_phy_package_join(struct phy_device *phydev, size_t priv_size)
+-{
+- struct device_node *node = phydev->mdio.dev.of_node;
+- struct device_node *package_node;
+- u32 base_addr;
+- int ret;
+-
+- if (!node)
+- return -EINVAL;
+-
+- package_node = of_get_parent(node);
+- if (!package_node)
+- return -EINVAL;
+-
+- if (!of_node_name_eq(package_node, "ethernet-phy-package")) {
+- ret = -EINVAL;
+- goto exit;
+- }
+-
+- if (of_property_read_u32(package_node, "reg", &base_addr)) {
+- ret = -EINVAL;
+- goto exit;
+- }
+-
+- ret = phy_package_join(phydev, base_addr, priv_size);
+- if (ret)
+- goto exit;
+-
+- phydev->shared->np = package_node;
+-
+- return 0;
+-exit:
+- of_node_put(package_node);
+- return ret;
+-}
+-EXPORT_SYMBOL_GPL(of_phy_package_join);
+-
+-/**
+- * phy_package_leave - leave a common PHY group
+- * @phydev: target phy_device struct
+- *
+- * This leaves a PHY group created by phy_package_join(). If this phydev
+- * was the last user of the shared data between the group, this data is
+- * freed. Resets the phydev->shared pointer to NULL.
+- */
+-void phy_package_leave(struct phy_device *phydev)
+-{
+- struct phy_package_shared *shared = phydev->shared;
+- struct mii_bus *bus = phydev->mdio.bus;
+-
+- if (!shared)
+- return;
+-
+- /* Decrease the node refcount on leave if present */
+- if (shared->np)
+- of_node_put(shared->np);
+-
+- if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
+- bus->shared[shared->base_addr] = NULL;
+- mutex_unlock(&bus->shared_lock);
+- kfree(shared->priv);
+- kfree(shared);
+- }
+-
+- phydev->shared = NULL;
+-}
+-EXPORT_SYMBOL_GPL(phy_package_leave);
+-
+-static void devm_phy_package_leave(struct device *dev, void *res)
+-{
+- phy_package_leave(*(struct phy_device **)res);
+-}
+-
+-/**
+- * devm_phy_package_join - resource managed phy_package_join()
+- * @dev: device that is registering this PHY package
+- * @phydev: target phy_device struct
+- * @base_addr: cookie and base PHY address of PHY package for offset
+- * calculation of global register access
+- * @priv_size: if non-zero allocate this amount of bytes for private data
+- *
+- * Managed phy_package_join(). Shared storage fetched by this function,
+- * phy_package_leave() is automatically called on driver detach. See
+- * phy_package_join() for more information.
+- */
+-int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+- int base_addr, size_t priv_size)
+-{
+- struct phy_device **ptr;
+- int ret;
+-
+- ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
+- GFP_KERNEL);
+- if (!ptr)
+- return -ENOMEM;
+-
+- ret = phy_package_join(phydev, base_addr, priv_size);
+-
+- if (!ret) {
+- *ptr = phydev;
+- devres_add(dev, ptr);
+- } else {
+- devres_free(ptr);
+- }
+-
+- return ret;
+-}
+-EXPORT_SYMBOL_GPL(devm_phy_package_join);
+-
+-/**
+- * devm_of_phy_package_join - resource managed of_phy_package_join()
+- * @dev: device that is registering this PHY package
+- * @phydev: target phy_device struct
+- * @priv_size: if non-zero allocate this amount of bytes for private data
+- *
+- * Managed of_phy_package_join(). Shared storage fetched by this function,
+- * phy_package_leave() is automatically called on driver detach. See
+- * of_phy_package_join() for more information.
+- */
+-int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
+- size_t priv_size)
+-{
+- struct phy_device **ptr;
+- int ret;
+-
+- ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
+- GFP_KERNEL);
+- if (!ptr)
+- return -ENOMEM;
+-
+- ret = of_phy_package_join(phydev, priv_size);
+-
+- if (!ret) {
+- *ptr = phydev;
+- devres_add(dev, ptr);
+- } else {
+- devres_free(ptr);
+- }
+-
+- return ret;
+-}
+-EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
+-
+-/**
+ * phy_detach - detach a PHY device from its network device
+ * @phydev: target phy_device struct
+ *
+--- /dev/null
++++ b/drivers/net/phy/phy_package.c
+@@ -0,0 +1,244 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * PHY package support
++ */
++
++#include <linux/of.h>
++#include <linux/phy.h>
++
++/**
++ * phy_package_join - join a common PHY group
++ * @phydev: target phy_device struct
++ * @base_addr: cookie and base PHY address of PHY package for offset
++ * calculation of global register access
++ * @priv_size: if non-zero allocate this amount of bytes for private data
++ *
++ * This joins a PHY group and provides a shared storage for all phydevs in
++ * this group. This is intended to be used for packages which contain
++ * more than one PHY, for example a quad PHY transceiver.
++ *
++ * The base_addr parameter serves as cookie which has to have the same values
++ * for all members of one group and as the base PHY address of the PHY package
++ * for offset calculation to access generic registers of a PHY package.
++ * Usually, one of the PHY addresses of the different PHYs in the package
++ * provides access to these global registers.
++ * The address which is given here, will be used in the phy_package_read()
++ * and phy_package_write() convenience functions as base and added to the
++ * passed offset in those functions.
++ *
++ * This will set the shared pointer of the phydev to the shared storage.
++ * If this is the first call for a this cookie the shared storage will be
++ * allocated. If priv_size is non-zero, the given amount of bytes are
++ * allocated for the priv member.
++ *
++ * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
++ * with the same cookie but a different priv_size is an error.
++ */
++int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size)
++{
++ struct mii_bus *bus = phydev->mdio.bus;
++ struct phy_package_shared *shared;
++ int ret;
++
++ if (base_addr < 0 || base_addr >= PHY_MAX_ADDR)
++ return -EINVAL;
++
++ mutex_lock(&bus->shared_lock);
++ shared = bus->shared[base_addr];
++ if (!shared) {
++ ret = -ENOMEM;
++ shared = kzalloc(sizeof(*shared), GFP_KERNEL);
++ if (!shared)
++ goto err_unlock;
++ if (priv_size) {
++ shared->priv = kzalloc(priv_size, GFP_KERNEL);
++ if (!shared->priv)
++ goto err_free;
++ shared->priv_size = priv_size;
++ }
++ shared->base_addr = base_addr;
++ shared->np = NULL;
++ refcount_set(&shared->refcnt, 1);
++ bus->shared[base_addr] = shared;
++ } else {
++ ret = -EINVAL;
++ if (priv_size && priv_size != shared->priv_size)
++ goto err_unlock;
++ refcount_inc(&shared->refcnt);
++ }
++ mutex_unlock(&bus->shared_lock);
++
++ phydev->shared = shared;
++
++ return 0;
++
++err_free:
++ kfree(shared);
++err_unlock:
++ mutex_unlock(&bus->shared_lock);
++ return ret;
++}
++EXPORT_SYMBOL_GPL(phy_package_join);
++
++/**
++ * of_phy_package_join - join a common PHY group in PHY package
++ * @phydev: target phy_device struct
++ * @priv_size: if non-zero allocate this amount of bytes for private data
++ *
++ * This is a variant of phy_package_join for PHY package defined in DT.
++ *
++ * The parent node of the @phydev is checked as a valid PHY package node
++ * structure (by matching the node name "ethernet-phy-package") and the
++ * base_addr for the PHY package is passed to phy_package_join.
++ *
++ * With this configuration the shared struct will also have the np value
++ * filled to use additional DT defined properties in PHY specific
++ * probe_once and config_init_once PHY package OPs.
++ *
++ * Returns < 0 on error, 0 on success. Esp. calling phy_package_join()
++ * with the same cookie but a different priv_size is an error. Or a parent
++ * node is not detected or is not valid or doesn't match the expected node
++ * name for PHY package.
++ */
++int of_phy_package_join(struct phy_device *phydev, size_t priv_size)
++{
++ struct device_node *node = phydev->mdio.dev.of_node;
++ struct device_node *package_node;
++ u32 base_addr;
++ int ret;
++
++ if (!node)
++ return -EINVAL;
++
++ package_node = of_get_parent(node);
++ if (!package_node)
++ return -EINVAL;
++
++ if (!of_node_name_eq(package_node, "ethernet-phy-package")) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ if (of_property_read_u32(package_node, "reg", &base_addr)) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = phy_package_join(phydev, base_addr, priv_size);
++ if (ret)
++ goto exit;
++
++ phydev->shared->np = package_node;
++
++ return 0;
++exit:
++ of_node_put(package_node);
++ return ret;
++}
++EXPORT_SYMBOL_GPL(of_phy_package_join);
++
++/**
++ * phy_package_leave - leave a common PHY group
++ * @phydev: target phy_device struct
++ *
++ * This leaves a PHY group created by phy_package_join(). If this phydev
++ * was the last user of the shared data between the group, this data is
++ * freed. Resets the phydev->shared pointer to NULL.
++ */
++void phy_package_leave(struct phy_device *phydev)
++{
++ struct phy_package_shared *shared = phydev->shared;
++ struct mii_bus *bus = phydev->mdio.bus;
++
++ if (!shared)
++ return;
++
++ /* Decrease the node refcount on leave if present */
++ if (shared->np)
++ of_node_put(shared->np);
++
++ if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
++ bus->shared[shared->base_addr] = NULL;
++ mutex_unlock(&bus->shared_lock);
++ kfree(shared->priv);
++ kfree(shared);
++ }
++
++ phydev->shared = NULL;
++}
++EXPORT_SYMBOL_GPL(phy_package_leave);
++
++static void devm_phy_package_leave(struct device *dev, void *res)
++{
++ phy_package_leave(*(struct phy_device **)res);
++}
++
++/**
++ * devm_phy_package_join - resource managed phy_package_join()
++ * @dev: device that is registering this PHY package
++ * @phydev: target phy_device struct
++ * @base_addr: cookie and base PHY address of PHY package for offset
++ * calculation of global register access
++ * @priv_size: if non-zero allocate this amount of bytes for private data
++ *
++ * Managed phy_package_join(). Shared storage fetched by this function,
++ * phy_package_leave() is automatically called on driver detach. See
++ * phy_package_join() for more information.
++ */
++int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
++ int base_addr, size_t priv_size)
++{
++ struct phy_device **ptr;
++ int ret;
++
++ ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
++ GFP_KERNEL);
++ if (!ptr)
++ return -ENOMEM;
++
++ ret = phy_package_join(phydev, base_addr, priv_size);
++
++ if (!ret) {
++ *ptr = phydev;
++ devres_add(dev, ptr);
++ } else {
++ devres_free(ptr);
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(devm_phy_package_join);
++
++/**
++ * devm_of_phy_package_join - resource managed of_phy_package_join()
++ * @dev: device that is registering this PHY package
++ * @phydev: target phy_device struct
++ * @priv_size: if non-zero allocate this amount of bytes for private data
++ *
++ * Managed of_phy_package_join(). Shared storage fetched by this function,
++ * phy_package_leave() is automatically called on driver detach. See
++ * of_phy_package_join() for more information.
++ */
++int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
++ size_t priv_size)
++{
++ struct phy_device **ptr;
++ int ret;
++
++ ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
++ GFP_KERNEL);
++ if (!ptr)
++ return -ENOMEM;
++
++ ret = of_phy_package_join(phydev, priv_size);
++
++ if (!ret) {
++ *ptr = phydev;
++ devres_add(dev, ptr);
++ } else {
++ devres_free(ptr);
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
--- /dev/null
+From e9ee1dd2ff2c130b2fb2bd01936224a8a6b49a7e Mon Sep 17 00:00:00 2001
+Date: Mon, 3 Mar 2025 21:15:09 +0100
+Subject: [PATCH] v6.15: net: phy: add getters for public members in struct
+ phy_package_shared
+
+Add getters for public members, this prepares for making struct
+phy_package_shared private to phylib. Declare the getters in a new header
+file phylib.h, which will be used by PHY drivers only.
+
+---
+ drivers/net/phy/phy_package.c | 14 ++++++++++++++
+ drivers/net/phy/phylib.h | 15 +++++++++++++++
+ 2 files changed, 29 insertions(+)
+ create mode 100644 drivers/net/phy/phylib.h
+
+--- a/drivers/net/phy/phy_package.c
++++ b/drivers/net/phy/phy_package.c
+@@ -6,6 +6,20 @@
+ #include <linux/of.h>
+ #include <linux/phy.h>
+
++#include "phylib.h"
++
++struct device_node *phy_package_get_node(struct phy_device *phydev)
++{
++ return phydev->shared->np;
++}
++EXPORT_SYMBOL_GPL(phy_package_get_node);
++
++void *phy_package_get_priv(struct phy_device *phydev)
++{
++ return phydev->shared->priv;
++}
++EXPORT_SYMBOL_GPL(phy_package_get_priv);
++
+ /**
+ * phy_package_join - join a common PHY group
+ * @phydev: target phy_device struct
+--- /dev/null
++++ b/drivers/net/phy/phylib.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * phylib header
++ */
++
++#ifndef __PHYLIB_H
++#define __PHYLIB_H
++
++struct device_node;
++struct phy_device;
++
++struct device_node *phy_package_get_node(struct phy_device *phydev);
++void *phy_package_get_priv(struct phy_device *phydev);
++
++#endif /* __PHYLIB_H */
-From ae682f13d308682232069e5150e884fc10160598 Mon Sep 17 00:00:00 2001
+From 7b1c4e22532ded6b20ee41936fa38b5ca1e61ff9 Mon Sep 17 00:00:00 2001
Date: Mon, 29 Jan 2024 17:57:20 +0800
Subject: [PATCH] dt-bindings: net: Document Qualcomm QCA8084 PHY package
Change-Id: Idb2338d2673152cbd3c57e95968faa59e9d4a80f
+Alex G: Update to match the patches that will be upstream.
---
- .../devicetree/bindings/net/qcom,qca8084.yaml | 198 ++++++++++++++++++
- include/dt-bindings/net/qcom,qca808x.h | 14 ++
- 2 files changed, 212 insertions(+)
+ .../devicetree/bindings/net/qcom,qca8084.yaml | 488 ++++++++++++++++++
+ include/dt-bindings/net/qcom,qca808x.h | 14 +
+ 2 files changed, 502 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 @@
+@@ -0,0 +1,488 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+
+description:
-+ Qualcomm QCA8084 is a four-port Ethernet transceiver, the
-+ Ethernet port supports link speed 10/100/1000/2500 Mbps.
++ Qualcomm QCA8084 is PHY package of 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
+ | | |
+ Ref 50M clk +--------+ | |
+ ------------>| | clk & rst | |
-+ GPIO Reset |QCA8K_CC+------------+ |
++ 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).
++ 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
+ - 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.
++ 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:
+ 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.
++ 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]+)?$:
++ ^ethernet-phy@[a-f0-9]+$:
++ unevaluatedProperties: false
+ $ref: ethernet-phy.yaml#
+
+ properties:
+ compatible:
+ const: ethernet-phy-id004d.d180
+
++ qcom,xpcs-channel:
++ description:
++ When PCS1 works on the interface mode 10G-QXGMII, the integrated
++ XPCS including 4 channels is used to connected with the Quad PHYs,
++ each PHY needs to be specified the XPCS channel ID to deliver the
++ PHY link status to the XPCS.
++ $ref: /schemas/types.yaml#/definitions/uint32
++ enum: [0, 1, 2, 3]
++
+ required:
+ - compatible
+ - reg
+ - clocks
+ - resets
+
-+ unevaluatedProperties: false
++ ^pcs-phy@[a-f0-9]+$:
++ type: object
++ additionalProperties: false
++ description:
++ PCS device has the independent MDIO address, which controls
++ the interface mode used and provides the clocks such as
++ 312.5 MHZ as RX and TX root clocks to the integrated clock
++ controller.
++ properties:
++ compatible:
++ const: qcom,qca8k-pcs-phy
++
++ reg:
++ items:
++ - description: PCS MDIO address.
++
++ clocks:
++ items:
++ - description: PCS clock.
++ - description: PCS RX root clock.
++ - description: PCS TX root clock.
++
++ clock-names:
++ items:
++ - const: pcs
++ - const: pcs_rx_root
++ - const: pcs_tx_root
++
++ resets:
++ items:
++ - description: PCS reset.
++
++ required:
++ - compatible
++ - reg
++ - clocks
++ - resets
++
++ ^xpcs-phy@[a-f0-9]+$:
++ type: object
++ additionalProperties: false
++ description:
++ XPCS device has the independent MDIO address, which includes 4
++ channels to connect with Quad PHYs.
++ properties:
++ compatible:
++ const: qcom,qca8k-xpcs-phy
++
++ reg:
++ items:
++ - description: XPCS MDIO address.
++
++ resets:
++ items:
++ - description: XPCS reset.
++
++ '#address-cells':
++ const: 1
++
++ '#size-cells':
++ const: 0
++
++ required:
++ - compatible
++ - reg
++ - resets
++ - '#address-cells'
++ - '#size-cells'
++
++ patternProperties:
++ "^channel@[0-3]+$":
++ type: object
++ additionalProperties: false
++ description:
++ XPCS is used to support 10G-QXGMII mode, which inlcudes 4 channels
++ to be connected with Quad PHYs, each channels has the dedicated
++ clocks and resets from the integrated clock controller of QCA8084.
++
++ properties:
++ reg:
++ items:
++ - description: XPCS channel ID
++
++ clocks:
++ items:
++ - description: XPCS XGMII RX clock
++ - description: XPCS XGMII TX clock
++ - description: XPCS RX clock
++ - description: XPCS TX clock
++ - description: Port RX clock
++ - description: Port TX clock
++ - description: RX source clock
++ - description: TX source clock
++
++ clock-names:
++ items:
++ - const: xgmii_rx
++ - const: xgmii_tx
++ - const: xpcs_rx
++ - const: xpcs_tx
++ - const: port_rx
++ - const: port_tx
++ - const: rx_src
++ - const: tx_src
++
++ resets:
++ items:
++ - description: XPCS XGMII RX reset
++ - description: XPCS XGMII TX reset
++ - description: XPCS RX reset
++ - description: XPCS TX reset
++ - description: Port RX reset
++ - description: Port TX reset
++
++ reset-names:
++ items:
++ - const: xgmii_rx
++ - const: xgmii_tx
++ - const: xpcs_rx
++ - const: xpcs_tx
++ - const: port_rx
++ - const: port_tx
++
++ required:
++ - reg
++ - clocks
++ - clock-names
++ - resets
++ - reset-names
+
+required:
+ - compatible
+ - clock-names
+ - resets
+
++allOf:
++ - $ref: ethernet-phy-package.yaml#
++
+unevaluatedProperties: false
+
+examples:
+ "cnoc_ahb",
+ "mdio_ahb";
+ resets = <&qca8k_nsscc NSS_CC_GEPHY_FULL_ARES>;
-+ qcom,package-mode = <QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_MAC>;
++ qcom,package-mode = <QCA808X_PCS1_10G_QXGMII_PCS0_UNUNSED>;
+ qcom,phy-addr-fixup = <1 2 3 4 5 6 7>;
+
+ ethernet-phy@1 {
+ reg = <1>;
+ clocks = <&qca8k_nsscc NSS_CC_GEPHY0_SYS_CLK>;
+ resets = <&qca8k_nsscc NSS_CC_GEPHY0_SYS_ARES>;
++ qcom,xpcs-channel = <0>;
+ };
+
+ ethernet-phy@2 {
+ reg = <2>;
+ clocks = <&qca8k_nsscc NSS_CC_GEPHY1_SYS_CLK>;
+ resets = <&qca8k_nsscc NSS_CC_GEPHY1_SYS_ARES>;
++ qcom,xpcs-channel = <1>;
+ };
+
+ ethernet-phy@3 {
+ reg = <3>;
+ clocks = <&qca8k_nsscc NSS_CC_GEPHY2_SYS_CLK>;
+ resets = <&qca8k_nsscc NSS_CC_GEPHY2_SYS_ARES>;
++ qcom,xpcs-channel = <2>;
+ };
+
+ ethernet-phy@4 {
+ reg = <4>;
+ clocks = <&qca8k_nsscc NSS_CC_GEPHY3_SYS_CLK>;
+ resets = <&qca8k_nsscc NSS_CC_GEPHY3_SYS_ARES>;
++ qcom,xpcs-channel = <3>;
++ };
++
++ pcs-phy@6 {
++ compatible = "qcom,qca8k-pcs-phy";
++ reg = <6>;
++ clocks = <&qca8k_nsscc NSS_CC_SRDS1_SYS_CLK>,
++ <&qca8k_uniphy1_tx312p5m>,
++ <&qca8k_uniphy1_rx312p5m>;
++ clock-names = "pcs", "pcs_rx_root", "pcs_tx_root";
++ resets = <&qca8k_nsscc NSS_CC_SRDS1_SYS_ARES>;
++ };
++
++ xpcs-phy@7 {
++ compatible = "qcom,qca8k-xpcs-phy";
++ reg = <7>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ resets = <&qca8k_nsscc NSS_CC_XPCS_ARES>;
++
++ channel@0 {
++ reg = <0>;
++ clocks = <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_XGMII_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_XGMII_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC1_GEPHY0_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC1_GEPHY0_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC1_RX_CLK_SRC>,
++ <&qca8k_nsscc NSS_CC_MAC1_TX_CLK_SRC>;
++ clock-names = "xgmii_rx",
++ "xgmii_tx",
++ "xpcs_rx",
++ "xpcs_tx",
++ "port_rx",
++ "port_tx",
++ "rx_src",
++ "tx_src";
++ resets = <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_XGMII_TX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_XGMII_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_TX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC1_GEPHY0_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC1_GEPHY0_TX_ARES>;
++ reset-names = "xgmii_rx",
++ "xgmii_tx",
++ "xpcs_rx",
++ "xpcs_tx",
++ "port_rx",
++ "port_tx";
++ };
++
++ channel@1 {
++ reg = <1>;
++ clocks = <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_XGMII_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_XGMII_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC2_GEPHY1_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC2_GEPHY1_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC2_RX_CLK_SRC>,
++ <&qca8k_nsscc NSS_CC_MAC2_TX_CLK_SRC>;
++ clock-names = "xgmii_rx",
++ "xgmii_tx",
++ "xpcs_rx",
++ "xpcs_tx",
++ "port_rx",
++ "port_tx",
++ "rx_src",
++ "tx_src";
++ resets = <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_XGMII_TX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_XGMII_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_TX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC2_GEPHY1_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC2_GEPHY1_TX_ARES>;
++ reset-names = "xgmii_rx",
++ "xgmii_tx",
++ "xpcs_rx",
++ "xpcs_tx",
++ "port_rx",
++ "port_tx";
++ };
++
++ channel@2 {
++ reg = <2>;
++ clocks = <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_XGMII_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_XGMII_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC3_GEPHY2_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC3_GEPHY2_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC3_RX_CLK_SRC>,
++ <&qca8k_nsscc NSS_CC_MAC3_TX_CLK_SRC>;
++ clock-names = "xgmii_rx",
++ "xgmii_tx",
++ "xpcs_rx",
++ "xpcs_tx",
++ "port_rx",
++ "port_tx",
++ "rx_src",
++ "tx_src";
++ resets = <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_XGMII_TX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_XGMII_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_TX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC3_GEPHY2_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC3_GEPHY2_TX_ARES>;
++ reset-names = "xgmii_rx",
++ "xgmii_tx",
++ "xpcs_rx",
++ "xpcs_tx",
++ "port_rx",
++ "port_tx";
++ };
++
++ channel@3 {
++ reg = <3>;
++ clocks = <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_XGMII_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_XGMII_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC4_GEPHY3_RX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC4_GEPHY3_TX_CLK>,
++ <&qca8k_nsscc NSS_CC_MAC4_RX_CLK_SRC>,
++ <&qca8k_nsscc NSS_CC_MAC4_TX_CLK_SRC>;
++ clock-names = "xgmii_rx",
++ "xgmii_tx",
++ "xpcs_rx",
++ "xpcs_tx",
++ "port_rx",
++ "port_tx",
++ "rx_src",
++ "tx_src";
++ resets = <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_XGMII_TX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_XGMII_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_TX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC4_GEPHY3_RX_ARES>,
++ <&qca8k_nsscc NSS_CC_MAC4_GEPHY3_TX_ARES>;
++ reset-names = "xgmii_rx",
++ "xgmii_tx",
++ "xpcs_rx",
++ "xpcs_tx",
++ "port_rx",
++ "port_tx";
++ };
+ };
+ };
+ };
-From 816bff9bcd2ff7c1e84dd14fc81c9c1bdaa609e7 Mon Sep 17 00:00:00 2001
+From 60c44842f9611be237ab3f68afe8ebf2d9595fb2 Mon Sep 17 00:00:00 2001
Date: Thu, 6 Apr 2023 18:09:07 +0800
Subject: [PATCH] net: phy: qca808x: Add QCA8084 ethernet phy support
Change-Id: I12555fa70662682474ab4432204405b5e752fef6
+Alex G: Update to match the patches that will be upstream.
---
- drivers/net/phy/qcom/qca808x.c | 62 ++++++++++++++++++++++++++++++++--
- 1 file changed, 60 insertions(+), 2 deletions(-)
+ drivers/net/phy/qcom/qca808x.c | 65 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 63 insertions(+), 2 deletions(-)
--- a/drivers/net/phy/qcom/qca808x.c
+++ b/drivers/net/phy/qcom/qca808x.c
MODULE_LICENSE("GPL");
struct qca808x_priv {
-@@ -153,7 +160,9 @@ static bool qca808x_is_prefer_master(str
+@@ -153,13 +160,18 @@ static bool qca808x_is_prefer_master(str
static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
{
}
static bool qca808x_is_1g_only(struct phy_device *phydev)
-@@ -273,6 +282,23 @@ static int qca808x_read_status(struct ph
+ {
+ int ret;
+
++ if (!phydev_id_compare(phydev, QCA8081_PHY_ID))
++ return false;
++
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
+ if (ret < 0)
+ return true;
+@@ -273,6 +285,23 @@ static int qca808x_read_status(struct ph
return ret;
if (phydev->link) {
if (phydev->speed == SPEED_2500)
phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
else
-@@ -352,6 +378,18 @@ static int qca808x_cable_test_start(stru
+@@ -352,6 +381,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);
return 0;
}
-@@ -651,12 +689,32 @@ static struct phy_driver qca808x_driver[
+@@ -651,12 +692,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,
-From 5a57611512593212b7fd9c23b4d96486bab6dee3 Mon Sep 17 00:00:00 2001
+From c052b9a4ab869cc54976402b3f9dbdef5bdb9f27 Mon Sep 17 00:00:00 2001
Date: Wed, 8 Nov 2023 16:18:02 +0800
Subject: [PATCH] net: phy: qca808x: Add config_init function for QCA8084
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
+@@ -663,6 +672,34 @@ static int qca808x_led_polarity_set(stru
active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
}
static struct phy_driver qca808x_driver[] = {
{
/* Qualcomm QCA8081 */
-@@ -708,6 +745,7 @@ static struct phy_driver qca808x_driver[
+@@ -711,6 +748,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,
-From d1f2a1810af1833196934977f57607432fda46b4 Mon Sep 17 00:00:00 2001
+From aec49c172cd9c739c1d97ff2d42b9718bb20b609 Mon Sep 17 00:00:00 2001
Date: Wed, 8 Nov 2023 18:01:14 +0800
Subject: [PATCH] net: phy: qca808x: Add link_change_notify function for
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
+@@ -700,6 +708,49 @@ static int qca8084_config_init(struct ph
QCA8084_MSE_THRESHOLD_2P5G_VAL);
}
static struct phy_driver qca808x_driver[] = {
{
/* Qualcomm QCA8081 */
-@@ -746,6 +797,7 @@ static struct phy_driver qca808x_driver[
+@@ -749,6 +800,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,
-From c17f19be3bec0bf5467f4e14a21573836910f671 Mon Sep 17 00:00:00 2001
+From cea8043def0c0867370c2efd5a1cd73bf4d3e5ba Mon Sep 17 00:00:00 2001
Date: Wed, 29 Nov 2023 15:21:22 +0800
Subject: [PATCH] net: phy: qca808x: Add register access support routines for
-From 485f973c5b1d889bd1f48a188137d80d45004991 Mon Sep 17 00:00:00 2001
+From a7fe2c13f3188bf01b60fb15063d028c76dd2f1a Mon Sep 17 00:00:00 2001
Date: Mon, 29 Jan 2024 10:51:38 +0800
Subject: [PATCH] net: phy: qca808x: Add QCA8084 probe function
Change-Id: I2251b9c5c398a21a4ef547a727189a934ad3a44c
+Alex G: include <linux/reset.h>
+ include "phylib.h" for phy_package_*() declarations
+ select PHY_PACKAGE in Kconfig
+ use phy_package_get_node() instead of phylib->shared->np
+
+freckup c89414adf2ec7c
+
---
- drivers/net/phy/qcom/qca808x.c | 91 ++++++++++++++++++++++++++++++++++
- 1 file changed, 91 insertions(+)
+ drivers/net/phy/qcom/Kconfig | 1 +
+ drivers/net/phy/qcom/qca808x.c | 92 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 93 insertions(+)
+--- a/drivers/net/phy/qcom/Kconfig
++++ b/drivers/net/phy/qcom/Kconfig
+@@ -18,6 +18,7 @@ config QCA83XX_PHY
+ config QCA808X_PHY
+ tristate "Qualcomm QCA808x PHYs"
+ select QCOM_NET_PHYLIB
++ select PHY_PACKAGE
+ help
+ Currently supports the QCA8081 model
+
--- a/drivers/net/phy/qcom/qca808x.c
+++ b/drivers/net/phy/qcom/qca808x.c
-@@ -2,6 +2,8 @@
+@@ -2,7 +2,11 @@
#include <linux/phy.h>
#include <linux/module.h>
+#include <linux/of.h>
++#include <linux/reset.h>
+#include <linux/clk.h>
++#include "../phylib.h"
#include "qcom.h"
-@@ -127,6 +129,21 @@
+ /* ADC threshold */
+@@ -127,6 +131,21 @@
#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");
-@@ -836,6 +853,79 @@ static void qca8084_link_change_notify(s
+@@ -839,6 +858,78 @@ 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;
++ struct device_node *np = phy_package_get_node(phydev);
+ 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",
++ ret = of_property_read_u32_array(np, "qcom,phy-addr-fixup",
+ addr, ARRAY_SIZE(addr));
+ if (ret && ret != -EINVAL)
+ return ret;
static struct phy_driver qca808x_driver[] = {
{
/* Qualcomm QCA8081 */
-@@ -886,6 +976,7 @@ static struct phy_driver qca808x_driver[
+@@ -889,6 +980,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,
-From 685566f8b765f522b7f4d4deb06bf84a557dc4ac Mon Sep 17 00:00:00 2001
+From 57379fe257895b374d35ce6578ecd62ce1cc1a4d Mon Sep 17 00:00:00 2001
Date: Tue, 9 Apr 2024 16:30:55 +0800
Subject: [PATCH] net: phy: qca808x: Add package clocks and resets for QCA8084
Change-Id: I254d0aa0a1155d3618c6f1fc7d7a5b6ecadccbaa
+Alex G: Use accessors for struct phy_package_shared
+ Update to match the patches that will be upstream.
---
- drivers/net/phy/qcom/qca808x.c | 67 ++++++++++++++++++++++++++++++++--
- 1 file changed, 64 insertions(+), 3 deletions(-)
+ drivers/net/phy/qcom/qca808x.c | 74 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 71 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
+@@ -150,10 +150,39 @@ MODULE_DESCRIPTION("Qualcomm Atheros QCA
MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
MODULE_LICENSE("GPL");
+ TLMM_AHB_CLK,
+ CNOC_AHB_CLK,
+ MDIO_AHB_CLK,
++ MDIO_MASTER_AHB_CLK,
++ SWITCH_CORE_CLK,
+ PACKAGE_CLK_MAX
+};
+
+ [TLMM_AHB_CLK] = "tlmm_ahb",
+ [CNOC_AHB_CLK] = "cnoc_ahb",
+ [MDIO_AHB_CLK] = "mdio_ahb",
++ [MDIO_MASTER_AHB_CLK] = "mdio_master_ahb",
++ [SWITCH_CORE_CLK] = "switch_core",
+};
+
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
+@@ -858,11 +887,24 @@ 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;
+ struct device_node *np = phy_package_get_node(phydev);
- int ret, clear, set;
+ struct qca808x_shared_priv *shared_priv;
+ struct reset_control *rstc;
/* 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
+@@ -893,17 +935,43 @@ 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]);
+ if (ret)
+ return ret;
+
-+ shared_priv = shared->priv;
++ shared_priv = phy_package_get_priv(phydev);
+ 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]);
++ clk = of_clk_get_by_name(np, qca8084_package_clk_name[i]);
++ if (IS_ERR(clk)) {
++ if (PTR_ERR(clk) == -EINVAL)
++ clk = NULL;
++ else
++ 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);
++ rstc = of_reset_control_get_exclusive(np, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(&phydev->mdio.dev, PTR_ERR(rstc),
+ "package reset not ready\n");
-From bf779b10b00fd79267d0ef625ae246df59ee23bd Mon Sep 17 00:00:00 2001
+From d39dc53424bcc778f1e468015490577e7bf0c7b6 Mon Sep 17 00:00:00 2001
Date: Thu, 25 Jan 2024 17:13:24 +0800
Subject: [PATCH] net: phy: qca808x: Add QCA8084 package init function
Change-Id: I63d4b22d2a70ee713cc6a6818b0f3c7aa098a5f5
+Alex G: Use phy_package_get_*() accessors
+ Update to match the patches that will be upstream.
---
- drivers/net/phy/qcom/qca808x.c | 115 +++++++++++++++++++++++++++++++++
- 1 file changed, 115 insertions(+)
+ drivers/net/phy/qcom/qca808x.c | 118 +++++++++++++++++++++++++++++++++
+ 1 file changed, 118 insertions(+)
--- a/drivers/net/phy/qcom/qca808x.c
+++ b/drivers/net/phy/qcom/qca808x.c
#include <linux/phy.h>
#include <linux/module.h>
#include <linux/of.h>
-@@ -145,6 +146,13 @@
+@@ -146,6 +147,12 @@
#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 {
+@@ -168,6 +175,7 @@ struct qca808x_priv {
};
struct qca808x_shared_priv {
struct clk *clk[PACKAGE_CLK_MAX];
};
-@@ -808,10 +817,107 @@ static int qca808x_led_polarity_set(stru
+@@ -816,10 +824,111 @@ static int qca808x_led_polarity_set(stru
active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
}
+ if (ret)
+ return ret;
+
++ ret = clk_prepare_enable(shared_priv->clk[SWITCH_CORE_CLK]);
++ if (ret)
++ return ret;
++
+ ret = clk_prepare_enable(shared_priv->clk[APB_BRIDGE_CLK]);
+ if (ret)
+ return ret;
+ if (ret)
+ return ret;
+
++ ret = clk_prepare_enable(shared_priv->clk[MDIO_MASTER_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;
++ shared_priv = phy_package_get_priv(phydev);
+ switch (shared_priv->package_mode) {
+ case QCA808X_PCS1_10G_QXGMII_PCS0_UNUNSED:
+ mode = QCA8084_WORK_MODE_QXGMII;
+ 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);
+ return ret;
+
+ usleep_range(10000, 11000);
-+ return ret;
++
++ /* Initialize the PHY package clock and reset, which is the
++ * necessary config sequence after GPIO reset on the PHY package.
++ */
++ return qca8084_package_clock_init(shared_priv);
+}
+
static int qca8084_config_init(struct phy_device *phydev)
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");
+@@ -954,6 +1063,15 @@ static int qca8084_phy_package_probe_onc
+ shared_priv->clk[i] = clk;
+ }
+ /* 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",
++ ret = of_property_read_u32(np, "qcom,package-mode",
+ &shared_priv->package_mode);
+ if (ret && ret != -EINVAL)
+ return ret;
+
- /* Deassert PHY package. */
- return reset_control_deassert(rstc);
- }
+ rstc = of_reset_control_get_exclusive(np, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(&phydev->mdio.dev, PTR_ERR(rstc),
--- /dev/null
+From d11eba3e178a9d42a579c656b2c9b643f4ce3e1e Mon Sep 17 00:00:00 2001
+Date: Mon, 23 Sep 2024 18:46:34 +0800
+Subject: [PATCH] net: phy: Add phy_package_remove_once helper
+
+QCA8084 PHY package needs to do the PHY package clean up,
+add phy_package_remove_once helper to support.
+
+Change-Id: I3cd73bc7be1b1d531435ef72f48db0682548decf
+---
+ include/linux/phy.h | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -366,6 +366,7 @@ struct phy_package_shared {
+ /* used as bit number in atomic bitops */
+ #define PHY_SHARED_F_INIT_DONE 0
+ #define PHY_SHARED_F_PROBE_DONE 1
++#define PHY_SHARED_F_REMOVE_DONE 2
+
+ /**
+ * struct mii_bus - Represents an MDIO bus
+@@ -2245,6 +2246,11 @@ static inline bool phy_package_probe_onc
+ return __phy_package_set_once(phydev, PHY_SHARED_F_PROBE_DONE);
+ }
+
++static inline bool phy_package_remove_once(struct phy_device *phydev)
++{
++ return __phy_package_set_once(phydev, PHY_SHARED_F_REMOVE_DONE);
++}
++
+ extern const struct bus_type mdio_bus_type;
+
+ struct mdio_board_info {
--- /dev/null
+From c12b79af730116936504afe97234f9afb6ac8fc0 Mon Sep 17 00:00:00 2001
+Date: Mon, 23 Sep 2024 20:28:24 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 SerDes probe and remove
+ functions
+
+QCA8084 PHY package integrates the XPCS and PCS, which is used
+to support 10G-QXGMII. XPCS includes 4 channels to connect with
+Quad PHY, and PCS controls the interface mode configured.
+
+XPCS and PCS are probed and removed by PHY package.
+
+Change-Id: Ided0a5cd4c996dc2a2a0d0598e930fab060caaf8
+Alex G: Use phy_package_get_*() accessors
+---
+ drivers/net/phy/qcom/Makefile | 2 +-
+ drivers/net/phy/qcom/qca8084_serdes.c | 249 ++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qca8084_serdes.h | 18 ++
+ drivers/net/phy/qcom/qca808x.c | 53 ++++++
+ 4 files changed, 321 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/phy/qcom/qca8084_serdes.c
+ create mode 100644 drivers/net/phy/qcom/qca8084_serdes.h
+
+--- a/drivers/net/phy/qcom/Makefile
++++ b/drivers/net/phy/qcom/Makefile
+@@ -2,5 +2,5 @@
+ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
+ obj-$(CONFIG_AT803X_PHY) += at803x.o
+ obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o
+-obj-$(CONFIG_QCA808X_PHY) += qca808x.o
++obj-$(CONFIG_QCA808X_PHY) += qca808x.o qca8084_serdes.o
+ obj-$(CONFIG_QCA807X_PHY) += qca807x.o
+--- /dev/null
++++ b/drivers/net/phy/qcom/qca8084_serdes.c
+@@ -0,0 +1,249 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#include <linux/clk.h>
++#include <linux/dev_printk.h>
++#include <linux/mdio.h>
++#include <linux/of.h>
++#include <linux/phy.h>
++#include <linux/property.h>
++#include <linux/reset.h>
++
++#include "qca8084_serdes.h"
++
++/* XPCS includes 4 channels, each channel has the different MMD ID for
++ * configuring auto-negotiation complete interrupt, mii-4bit, auto-
++ * negotiation capabilities and TX configuration for the connected PHY.
++ *
++ * MMD31 is for channel 0;
++ * MMD26 is for channel 1;
++ * MMD27 is for channel 2;
++ * MMD28 is for channel 3;
++ */
++#define QCA8084_CHANNEL_MAX 4
++
++enum pcs_clk_id {
++ PCS_CLK,
++ PCS_RX_ROOT_CLK,
++ PCS_TX_ROOT_CLK,
++ PCS_CLK_MAX
++};
++
++enum xpcs_clk_id {
++ XPCS_XGMII_RX_CLK,
++ XPCS_XGMII_TX_CLK,
++ XPCS_RX_CLK,
++ XPCS_TX_CLK,
++ XPCS_PORT_RX_CLK,
++ XPCS_PORT_TX_CLK,
++ XPCS_RX_SRC_CLK,
++ XPCS_TX_SRC_CLK,
++ XPCS_CLK_MAX
++};
++
++struct qca8084_xpcs_channel_priv {
++ int ch_id;
++ struct reset_control *rstcs;
++ struct clk *clks[XPCS_CLK_MAX];
++};
++
++struct qca8084_pcs_data {
++ struct reset_control *rstc;
++ struct clk *clks[PCS_CLK_MAX];
++};
++
++struct qca8084_xpcs_data {
++ struct reset_control *rstc;
++ struct qca8084_xpcs_channel_priv xpcs_ch[QCA8084_CHANNEL_MAX];
++};
++
++static const char *const xpcs_clock_names[XPCS_CLK_MAX] = {
++ [XPCS_XGMII_RX_CLK] = "xgmii_rx",
++ [XPCS_XGMII_TX_CLK] = "xgmii_tx",
++ [XPCS_RX_CLK] = "xpcs_rx",
++ [XPCS_TX_CLK] = "xpcs_tx",
++ [XPCS_PORT_RX_CLK] = "port_rx",
++ [XPCS_PORT_TX_CLK] = "port_tx",
++ [XPCS_RX_SRC_CLK] = "rx_src",
++ [XPCS_TX_SRC_CLK] = "tx_src",
++};
++
++static const char *const pcs_clock_names[PCS_CLK_MAX] = {
++ [PCS_CLK] = "pcs",
++ [PCS_RX_ROOT_CLK] = "pcs_rx_root",
++ [PCS_TX_ROOT_CLK] = "pcs_tx_root",
++};
++
++struct mdio_device *qca8084_package_pcs_probe(struct device_node *pcs_np)
++{
++ struct qca8084_pcs_data *pcs_data;
++ struct mdio_device *mdiodev;
++ struct reset_control *rstc;
++ struct device *dev;
++ struct clk *clk;
++ int i;
++
++ mdiodev = fwnode_mdio_find_device(of_fwnode_handle(pcs_np));
++ if (!mdiodev)
++ return ERR_PTR(-EPROBE_DEFER);
++
++ dev = &mdiodev->dev;
++ pcs_data = devm_kzalloc(dev, sizeof(*pcs_data), GFP_KERNEL);
++ if (!pcs_data) {
++ dev_err(dev, "Allocate PCS data failed\n");
++ return ERR_PTR(-ENOMEM);
++ }
++
++ rstc = devm_reset_control_get_exclusive(dev, NULL);
++ if (IS_ERR(rstc)) {
++ dev_err(dev, "Get PCS reset failed\n");
++ return ERR_CAST(rstc);
++ }
++
++ pcs_data->rstc = rstc;
++
++ for (i = 0; i < ARRAY_SIZE(pcs_clock_names); i++) {
++ clk = devm_clk_get(dev, pcs_clock_names[i]);
++ if (IS_ERR(clk)) {
++ dev_err(dev, "Failed to get the PCS clock ID %s\n",
++ pcs_clock_names[i]);
++ return ERR_CAST(clk);
++ }
++ pcs_data->clks[i] = clk;
++ }
++
++ mdiodev_set_drvdata(mdiodev, pcs_data);
++
++ return mdiodev;
++}
++
++struct mdio_device *qca8084_package_xpcs_probe(struct device_node *xpcs_np)
++{
++ struct qca8084_xpcs_data *xpcs_data;
++ struct mdio_device *mdiodev;
++ struct reset_control *rstc;
++ struct device_node *child;
++ struct device *dev;
++ struct clk *clk;
++ int i, j, node;
++
++ mdiodev = fwnode_mdio_find_device(of_fwnode_handle(xpcs_np));
++ if (!mdiodev)
++ return ERR_PTR(-EPROBE_DEFER);
++
++ dev = &mdiodev->dev;
++
++ xpcs_data = devm_kzalloc(dev, sizeof(*xpcs_data), GFP_KERNEL);
++ if (!xpcs_data) {
++ dev_err(dev, "Allocate XPCS data failed\n");
++ return ERR_PTR(-ENOMEM);
++ }
++
++ rstc = devm_reset_control_get_exclusive(dev, NULL);
++ if (IS_ERR(rstc)) {
++ dev_err(dev, "Get XPCS reset failed\n");
++ return ERR_CAST(rstc);
++ }
++
++ xpcs_data->rstc = rstc;
++
++ /* Sanity check the number of channel sub nodes */
++ node = of_get_available_child_count(xpcs_np);
++ if (node != QCA8084_CHANNEL_MAX)
++ return ERR_PTR(-EINVAL);
++
++ node = 0;
++ for_each_available_child_of_node(xpcs_np, child) {
++ struct qca8084_xpcs_channel_priv *ch_data;
++ u32 channel;
++
++ /* The subnode name must be 'channel'. */
++ if (!(of_node_name_eq(child, "channel")))
++ continue;
++
++ if (of_property_read_u32(child, "reg", &channel)) {
++ dev_err(dev, "%s: Failed to get reg\n",
++ child->full_name);
++
++ mdiodev = ERR_PTR(-EINVAL);
++ goto put_ch_clk_rst;
++ }
++
++ if (channel >= QCA8084_CHANNEL_MAX) {
++ dev_err(dev, "%s: Invalid reg %d\n",
++ child->full_name, channel);
++
++ mdiodev = ERR_PTR(-EINVAL);
++ goto put_ch_clk_rst;
++ }
++
++ ch_data = &xpcs_data->xpcs_ch[node];
++ ch_data->ch_id = channel;
++
++ ch_data->rstcs = of_reset_control_array_get_exclusive(child);
++ if (IS_ERR(ch_data->rstcs)) {
++ dev_err(dev, "%s: Failed to get reset\n",
++ child->full_name);
++
++ mdiodev = ERR_CAST(ch_data->rstcs);
++ goto put_ch_clk_rst;
++ }
++
++ for (j = 0; j < ARRAY_SIZE(xpcs_clock_names); j++) {
++ clk = of_clk_get_by_name(child, xpcs_clock_names[j]);
++ if (IS_ERR(clk)) {
++ dev_err(dev, "Failed to get the clock ID %s\n",
++ xpcs_clock_names[j]);
++ mdiodev = ERR_CAST(clk);
++ goto put_ch_child;
++ }
++ ch_data->clks[j] = clk;
++ }
++
++ node++;
++ }
++
++ mdiodev_set_drvdata(mdiodev, xpcs_data);
++
++ return mdiodev;
++
++put_ch_child:
++ node++;
++
++put_ch_clk_rst:
++ for (i = 0; i < node; i++) {
++ j--;
++ while (j >= 0) {
++ clk_put(xpcs_data->xpcs_ch[i].clks[j]);
++ j--;
++ }
++
++ j = ARRAY_SIZE(xpcs_clock_names);
++ }
++
++ for (i = 0; i < node; i++)
++ reset_control_put(xpcs_data->xpcs_ch[i].rstcs);
++
++ of_node_put(child);
++
++ return mdiodev;
++}
++
++void qca8084_package_xpcs_and_pcs_remove(struct mdio_device *xpcs_mdiodev,
++ struct mdio_device *pcs_mdiodev)
++{
++ struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
++ int i, j;
++
++ for (i = 0; i < ARRAY_SIZE(xpcs_data->xpcs_ch); i++) {
++ reset_control_put(xpcs_data->xpcs_ch[i].rstcs);
++
++ for (j = 0; j < ARRAY_SIZE(xpcs_data->xpcs_ch[i].clks); j++)
++ clk_put(xpcs_data->xpcs_ch[i].clks[j]);
++ }
++
++ mdio_device_put(xpcs_mdiodev);
++ mdio_device_put(pcs_mdiodev);
++}
+--- /dev/null
++++ b/drivers/net/phy/qcom/qca8084_serdes.h
+@@ -0,0 +1,18 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Driver for QCA8084 SerDes
++ *
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef _QCA8084_SERDES_H_
++#define _QCA8084_SERDES_H_
++
++#include <linux/mdio.h>
++#include <linux/of.h>
++
++struct mdio_device *qca8084_package_pcs_probe(struct device_node *pcs_np);
++struct mdio_device *qca8084_package_xpcs_probe(struct device_node *xpcs_np);
++void qca8084_package_xpcs_and_pcs_remove(struct mdio_device *xpcs_mdiodev,
++ struct mdio_device *pcs_mdiodev);
++#endif /* _QCA8084_SERDES_H_ */
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -8,6 +8,7 @@
+ #include <linux/clk.h>
+
+ #include "../phylib.h"
++#include "qca8084_serdes.h"
+ #include "qcom.h"
+
+ /* ADC threshold */
+@@ -172,11 +173,13 @@ enum {
+
+ struct qca808x_priv {
+ int led_polarity_mode;
++ int channel_id;
+ };
+
+ struct qca808x_shared_priv {
+ int package_mode;
+ struct clk *clk[PACKAGE_CLK_MAX];
++ struct mdio_device *mdiodev[2]; /* PCS and XPCS mdio device */
+ };
+
+ static const char *const qca8084_package_clk_name[PACKAGE_CLK_MAX] = {
+@@ -354,6 +357,8 @@ static int qca808x_probe(struct phy_devi
+ {
+ struct device *dev = &phydev->mdio.dev;
+ struct qca808x_priv *priv;
++ u32 ch_id = 0;
++ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -362,6 +367,14 @@ static int qca808x_probe(struct phy_devi
+ /* Init LED polarity mode to -1 */
+ priv->led_polarity_mode = -1;
+
++ /* DT property qcom,xpcs-channel" is optional and only available for
++ * 10G-QXGMII mode.
++ */
++ ret = of_property_read_u32(dev->of_node, "qcom,xpcs-channel", &ch_id);
++ if (ret && ret != -EINVAL)
++ return ret;
++
++ priv->channel_id = ch_id;
+ phydev->priv = priv;
+
+ return 0;
+@@ -1012,6 +1025,7 @@ static int qca8084_phy_package_probe_onc
+ struct device_node *np = phy_package_get_node(phydev);
+ struct qca808x_shared_priv *shared_priv;
+ struct reset_control *rstc;
++ struct device_node *child;
+ int i, ret, clear, set;
+ struct clk *clk;
+
+@@ -1072,6 +1086,26 @@ static int qca8084_phy_package_probe_onc
+ if (ret && ret != -EINVAL)
+ return ret;
+
++ for_each_available_child_of_node(np, child) {
++ struct mdio_device *mdiodev;
++
++ if (of_node_name_eq(child, "pcs-phy")) {
++ mdiodev = qca8084_package_pcs_probe(child);
++ if (IS_ERR(mdiodev))
++ return PTR_ERR(mdiodev);
++
++ shared_priv->mdiodev[0] = mdiodev;
++ }
++
++ if (of_node_name_eq(child, "xpcs-phy")) {
++ mdiodev = qca8084_package_xpcs_probe(child);
++ if (IS_ERR(mdiodev))
++ return PTR_ERR(mdiodev);
++
++ shared_priv->mdiodev[1] = mdiodev;
++ }
++ }
++
+ rstc = of_reset_control_get_exclusive(np, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(&phydev->mdio.dev, PTR_ERR(rstc),
+@@ -1081,6 +1115,14 @@ static int qca8084_phy_package_probe_onc
+ return reset_control_deassert(rstc);
+ }
+
++static void qca8084_phy_package_remove_once(struct phy_device *phydev)
++{
++ struct qca808x_shared_priv *shared_priv = phy_package_get_priv(phydev);;
++
++ qca8084_package_xpcs_and_pcs_remove(shared_priv->mdiodev[1],
++ shared_priv->mdiodev[0]);
++}
++
+ static int qca8084_probe(struct phy_device *phydev)
+ {
+ struct qca808x_shared_priv *shared_priv;
+@@ -1099,6 +1141,10 @@ static int qca8084_probe(struct phy_devi
+ return ret;
+ }
+
++ ret = qca808x_probe(phydev);
++ if (ret)
++ return ret;
++
+ /* Enable clock of PHY device, so that the PHY register
+ * can be accessed to get PHY features.
+ */
+@@ -1116,6 +1162,12 @@ static int qca8084_probe(struct phy_devi
+ return reset_control_deassert(rstc);
+ }
+
++static void qca8084_remove(struct phy_device *phydev)
++{
++ if (phy_package_remove_once(phydev))
++ qca8084_phy_package_remove_once(phydev);
++}
++
+ static struct phy_driver qca808x_driver[] = {
+ {
+ /* Qualcomm QCA8081 */
+@@ -1167,6 +1219,7 @@ static struct phy_driver qca808x_driver[
+ .config_init = qca8084_config_init,
+ .link_change_notify = qca8084_link_change_notify,
+ .probe = qca8084_probe,
++ .remove = qca8084_remove,
+ }, };
+
+ module_phy_driver(qca808x_driver);
--- /dev/null
+From d137b725f8f4a7d49a809dcd73c5b836495ec44d Mon Sep 17 00:00:00 2001
+Date: Mon, 23 Sep 2024 20:59:40 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 SerDes init function
+
+When QCA8084 works on 10G-QXGMII, the XPCS and PCS need to be
+configured in the PHY package init function.
+
+Change-Id: Iac48c44f0e80adf055fa9c2095e99a04ba24c4bb
+---
+ drivers/net/phy/qcom/qca8084_serdes.c | 374 ++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qca8084_serdes.h | 2 +
+ drivers/net/phy/qcom/qca808x.c | 11 +
+ 3 files changed, 387 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca8084_serdes.c
++++ b/drivers/net/phy/qcom/qca8084_serdes.c
+@@ -24,6 +24,92 @@
+ */
+ #define QCA8084_CHANNEL_MAX 4
+
++/* MII registers */
++#define PLL_POWER_ON_AND_RESET 0x0
++#define PCS_ANA_SW_RESET BIT(6)
++
++#define PLL_CONTROL 6
++#define PLL_CONTROL_CMLDIV2_IBSEL_MASK GENMASK(5, 4)
++
++/* MMD_PMAPMD registers */
++#define CDR_CONTRL 0x20
++#define SSC_FIX_MODE BIT(3)
++
++#define CALIBRATION4 0x78
++#define CALIBRATION_DONE BIT(7)
++
++#define MODE_CONTROL 0x11b
++#define MODE_CONTROL_SEL_MASK GENMASK(12, 8)
++#define MODE_CONTROL_XPCS 0x10
++#define MODE_CONTROL_SGMII_PLUS 0x8
++#define MODE_CONTROL_SGMII 0x4
++#define MODE_CONTROL_SGMII_SEL_MASK GENMASK(6, 4)
++#define MODE_CONTROL_SGMII_PHY 1
++#define MODE_CONTROL_SGMII_MAC 2
++
++#define QP_USXG_OPTION1 0x180
++#define QP_USXG_OPTION1_DATAPASS BIT(0)
++#define QP_USXG_OPTION1_DATAPASS_SGMII 0
++#define QP_USXG_OPTION1_DATAPASS_USXGMII 1
++
++#define BYPASS_TUNNING_IPG 0x189
++#define BYPASS_TUNNING_IPG_MASK GENMASK(11, 0)
++
++/* MDIO_MMD_PCS register */
++#define PCS_CONTROL2 0x7
++#define PCS_TYPE_MASK GENMASK(3, 0)
++#define PCS_TYPE_BASER 0
++
++#define PCS_EEE_CONTROL 0x14
++#define EEE_CAPABILITY BIT(6)
++
++#define PCS_STATUS1 0x20
++#define PCS_BASER_UP BIT(12)
++
++#define DIG_CTRL1 0x8000
++#define DIG_CTRL1_USXGMII_EN BIT(9)
++#define DIG_CTRL1_XPCS_RESET BIT(15)
++#define FIFO_RESET_CH0 BIT(10)
++#define FIFO_RESET_CH1_CH2_CH3 BIT(5)
++
++#define EEE_MODE_CONTROL 0x8006
++#define EEE_LCT_RES GENMASK(11, 8)
++#define EEE_SIGN BIT(6)
++#define EEE_LRX_EN BIT(1)
++#define EEE_LTX_EN BIT(0)
++
++#define PCS_TPC 0x8007
++#define PCS_QXGMII_MODE_MASK GENMASK(12, 10)
++#define PCS_QXGMII_EN 0x5
++
++#define EEE_RX_TIMER 0x8009
++#define EEE_RX_TIMER_100US_RES GENMASK(7, 0)
++#define EEE_RX_TIMER_RWR_RES GENMASK(12, 8)
++
++#define AM_LINK_TIMER 0x800a
++#define AM_LINK_TIMER_VAL 0x6018
++
++#define EEE_MODE_CONTROL1 0x800b
++#define TRANS_LPI_MODE BIT(0)
++#define TRANS_RX_LPI_MODE BIT(8)
++
++/* QXGMII channel MMD register */
++#define MII_CONTROL 0x0
++#define AUTO_NEGOTIATION_EN BIT(12)
++#define AUTO_NEGOTIATION_RESTART BIT(9)
++#define PCS_SPEED_2500 BIT(5)
++#define PCS_SPEED_1000 BIT(6)
++#define PCS_SPEED_100 BIT(13)
++#define PCS_SPEED_10 0
++
++#define DIG_CONTROL2 0x8001
++#define MII_BIT_CONTROL BIT(8)
++#define TX_CONFIG BIT(3)
++#define AUTO_NEGOTIATION_CMPLT_INTR BIT(0)
++
++#define XAUI_CONTROL 0x8004
++#define TX_IPG_CHECK_DISABLE BIT(0)
++
+ enum pcs_clk_id {
+ PCS_CLK,
+ PCS_RX_ROOT_CLK,
+@@ -76,6 +162,8 @@ static const char *const pcs_clock_names
+ [PCS_TX_ROOT_CLK] = "pcs_tx_root",
+ };
+
++static const int qca8084_xpcs_ch_mmd[QCA8084_CHANNEL_MAX] = { 31, 26, 27, 28 };
++
+ struct mdio_device *qca8084_package_pcs_probe(struct device_node *pcs_np)
+ {
+ struct qca8084_pcs_data *pcs_data;
+@@ -247,3 +335,289 @@ void qca8084_package_xpcs_and_pcs_remove
+ mdio_device_put(xpcs_mdiodev);
+ mdio_device_put(pcs_mdiodev);
+ }
++
++static int qca8084_pcs_set_interface_mode(struct mdio_device *mdio_dev,
++ phy_interface_t ifmode)
++{
++ int ret, hw_ifmode, data;
++
++ switch (ifmode) {
++ case PHY_INTERFACE_MODE_SGMII:
++ hw_ifmode = MODE_CONTROL_SGMII;
++ data = QP_USXG_OPTION1_DATAPASS_SGMII;
++ break;
++ case PHY_INTERFACE_MODE_2500BASEX:
++ hw_ifmode = MODE_CONTROL_SGMII_PLUS;
++ data = QP_USXG_OPTION1_DATAPASS_SGMII;
++ break;
++ case PHY_INTERFACE_MODE_10G_QXGMII:
++ hw_ifmode = MODE_CONTROL_XPCS;
++ data = QP_USXG_OPTION1_DATAPASS_USXGMII;
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ /* For PLL stable under high temperature */
++ ret = mdiodev_modify(mdio_dev, PLL_CONTROL,
++ PLL_CONTROL_CMLDIV2_IBSEL_MASK,
++ FIELD_PREP(PLL_CONTROL_CMLDIV2_IBSEL_MASK, 3));
++ if (ret)
++ return ret;
++
++ /* Configure the interface mode of PCS */
++ ret = mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, MODE_CONTROL,
++ MODE_CONTROL_SEL_MASK,
++ FIELD_PREP(MODE_CONTROL_SEL_MASK, hw_ifmode));
++ if (ret)
++ return ret;
++
++ /* Data pass selects SGMII or USXGMII */
++ return mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, QP_USXG_OPTION1,
++ QP_USXG_OPTION1_DATAPASS,
++ FIELD_PREP(QP_USXG_OPTION1_DATAPASS, data));
++}
++
++static int qca8084_do_calibration(struct mdio_device *mdio_dev)
++{
++ int ret;
++
++ ret = mdiodev_modify(mdio_dev, PLL_POWER_ON_AND_RESET,
++ PCS_ANA_SW_RESET, 0);
++ if (ret)
++ return ret;
++
++ usleep_range(10000, 11000);
++ ret = mdiodev_modify(mdio_dev, PLL_POWER_ON_AND_RESET,
++ PCS_ANA_SW_RESET, PCS_ANA_SW_RESET);
++ if (ret)
++ return ret;
++
++ /* Wait calibration done */
++ return read_poll_timeout(mdiodev_c45_read, ret,
++ (ret & CALIBRATION_DONE),
++ 100, 100000, true, mdio_dev,
++ MDIO_MMD_PMAPMD, CALIBRATION4);
++}
++
++
++static int qca8084_xpcs_set_mode(struct mdio_device *xpcs_mdiodev)
++{
++ int ret, val, i;
++
++ /* Configure BaseR mode */
++ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, PCS_CONTROL2,
++ PCS_TYPE_MASK,
++ FIELD_PREP(PCS_TYPE_MASK, PCS_TYPE_BASER));
++ if (ret)
++ return ret;
++
++ /* Wait BaseR link up */
++ ret = read_poll_timeout(mdiodev_c45_read, val,
++ (val & PCS_BASER_UP), 1000, 100000, true,
++ xpcs_mdiodev,
++ MDIO_MMD_PCS, PCS_STATUS1);
++ if (ret) {
++ dev_err(&xpcs_mdiodev->dev, "BaseR link failed!\n");
++ return ret;
++ }
++
++ /* Enable USXGMII mode */
++ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, DIG_CTRL1,
++ DIG_CTRL1_USXGMII_EN,
++ DIG_CTRL1_USXGMII_EN);
++ if (ret)
++ return ret;
++
++ /* Configure QXGMII mode */
++ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, PCS_TPC,
++ PCS_QXGMII_MODE_MASK,
++ FIELD_PREP(PCS_QXGMII_MODE_MASK,
++ PCS_QXGMII_EN));
++ if (ret)
++ return ret;
++
++ /* Configure AM interval */
++ ret = mdiodev_c45_write(xpcs_mdiodev, MDIO_MMD_PCS, AM_LINK_TIMER,
++ AM_LINK_TIMER_VAL);
++ if (ret)
++ return ret;
++
++ /* Reset XPCS */
++ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, DIG_CTRL1,
++ DIG_CTRL1_XPCS_RESET,
++ DIG_CTRL1_XPCS_RESET);
++ if (ret)
++ return ret;
++
++ /* Wait XPCS reset done */
++ ret = read_poll_timeout(mdiodev_c45_read, val,
++ !(val & DIG_CTRL1_XPCS_RESET),
++ 1000, 100000, true, xpcs_mdiodev,
++ MDIO_MMD_PCS, DIG_CTRL1);
++ if (ret) {
++ dev_err(&xpcs_mdiodev->dev, "XPCS reset failed!\n");
++ return ret;
++ }
++
++ /* Enable auto-negotiation complete interrupt, using mii-4bit
++ * and TX configureation of PHY side on all XPCS channels.
++ */
++ for (i = 0; i < QCA8084_CHANNEL_MAX; i++) {
++ ret = mdiodev_c45_modify(xpcs_mdiodev, qca8084_xpcs_ch_mmd[i],
++ DIG_CONTROL2,
++ (MII_BIT_CONTROL | TX_CONFIG |
++ AUTO_NEGOTIATION_CMPLT_INTR),
++ (TX_CONFIG | AUTO_NEGOTIATION_CMPLT_INTR));
++ if (ret)
++ return ret;
++
++ /* Enable auto-negotiation capability */
++ ret = mdiodev_c45_modify(xpcs_mdiodev, qca8084_xpcs_ch_mmd[i],
++ MII_CONTROL,
++ AUTO_NEGOTIATION_EN,
++ AUTO_NEGOTIATION_EN);
++ if (ret)
++ return ret;
++
++ /* Disable TX IPG check */
++ ret = mdiodev_c45_modify(xpcs_mdiodev, qca8084_xpcs_ch_mmd[i],
++ XAUI_CONTROL,
++ TX_IPG_CHECK_DISABLE,
++ TX_IPG_CHECK_DISABLE);
++ if (ret)
++ return ret;
++ }
++
++ /* Check EEE capability supported or not */
++ ret = mdiodev_c45_read(xpcs_mdiodev, MDIO_MMD_PCS, PCS_EEE_CONTROL);
++ if (ret < 0)
++ return ret;
++
++ if (ret & EEE_CAPABILITY) {
++ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
++ EEE_MODE_CONTROL,
++ EEE_LCT_RES | EEE_SIGN,
++ FIELD_PREP(EEE_LCT_RES, 1) | EEE_SIGN);
++ if (ret)
++ return ret;
++
++ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
++ EEE_RX_TIMER,
++ EEE_RX_TIMER_100US_RES | EEE_RX_TIMER_RWR_RES,
++ FIELD_PREP(EEE_RX_TIMER_100US_RES, 0xc8) |
++ FIELD_PREP(EEE_RX_TIMER_RWR_RES, 0x1c));
++ if (ret)
++ return ret;
++
++ /* Enable EEE LPI */
++ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
++ EEE_MODE_CONTROL1,
++ TRANS_LPI_MODE | TRANS_RX_LPI_MODE,
++ TRANS_LPI_MODE | TRANS_RX_LPI_MODE);
++ if (ret)
++ return ret;
++
++ /* Enable TX/RX LPI pattern */
++ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
++ EEE_MODE_CONTROL,
++ EEE_LRX_EN | EEE_LTX_EN,
++ EEE_LRX_EN | EEE_LTX_EN);
++ }
++
++ return ret;
++}
++
++static int qca8084_pcs_set_mode(struct mdio_device *xpcs_mdiodev,
++ struct mdio_device *pcs_mdiodev)
++{
++ struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
++ struct qca8084_pcs_data *pcs_data = mdiodev_get_drvdata(pcs_mdiodev);
++ struct qca8084_xpcs_channel_priv xpcs_ch;
++ int ret, channel;
++
++ /* Enable clock and de-assert for PCS. */
++ ret = clk_prepare_enable(pcs_data->clks[PCS_CLK]);
++ if (ret)
++ return ret;
++
++ ret = reset_control_deassert(pcs_data->rstc);
++ if (ret)
++ return ret;
++
++ /* IPG tunning selection for RX, TX and XGMII of all channels. */
++ ret = mdiodev_c45_modify(pcs_mdiodev, MDIO_MMD_PMAPMD,
++ BYPASS_TUNNING_IPG,
++ BYPASS_TUNNING_IPG_MASK, 0);
++ if (ret)
++ return ret;
++
++ reset_control_assert(xpcs_data->rstc);
++
++ ret = qca8084_pcs_set_interface_mode(pcs_mdiodev,
++ PHY_INTERFACE_MODE_10G_QXGMII);
++ if (ret)
++ return ret;
++
++ /* Reset of 4 channels */
++ for (channel = 0; channel < QCA8084_CHANNEL_MAX; channel++) {
++ xpcs_ch = xpcs_data->xpcs_ch[channel];
++ ret = reset_control_reset(xpcs_ch.rstcs);
++ if (ret)
++ return ret;
++ }
++
++ ret = qca8084_do_calibration(pcs_mdiodev);
++ if (ret) {
++ dev_err(&pcs_mdiodev->dev, "PCS calibration timeout!\n");
++ return ret;
++ }
++
++ /* Enable PCS SSC to fix mode */
++ ret = mdiodev_c45_modify(pcs_mdiodev, MDIO_MMD_PMAPMD,
++ CDR_CONTRL, SSC_FIX_MODE, SSC_FIX_MODE);
++ if (ret)
++ return ret;
++
++ return reset_control_deassert(xpcs_data->rstc);
++}
++
++static int qca8084_xpcs_clock_parent_set(struct mdio_device *xpcs_mdiodev,
++ struct mdio_device *pcs_mdiodev)
++{
++ struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
++ struct qca8084_pcs_data *pcs_data = mdiodev_get_drvdata(pcs_mdiodev);
++ struct qca8084_xpcs_channel_priv xpcs_ch;
++ int ret, channel;
++
++ for (channel = 0; channel < QCA8084_CHANNEL_MAX; channel++) {
++ xpcs_ch = xpcs_data->xpcs_ch[channel];
++ ret = clk_set_parent(xpcs_ch.clks[XPCS_RX_SRC_CLK],
++ pcs_data->clks[PCS_RX_ROOT_CLK]);
++ if (ret)
++ return ret;
++
++ ret = clk_set_parent(xpcs_ch.clks[XPCS_TX_SRC_CLK],
++ pcs_data->clks[PCS_TX_ROOT_CLK]);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++int qca8084_qxgmii_set_mode(struct mdio_device *xpcs_mdiodev,
++ struct mdio_device *pcs_mdiodev)
++{
++ int ret;
++
++ ret = qca8084_xpcs_clock_parent_set(xpcs_mdiodev, pcs_mdiodev);
++ if (ret)
++ return ret;
++
++ ret = qca8084_pcs_set_mode(xpcs_mdiodev, pcs_mdiodev);
++ if (ret)
++ return ret;
++
++ return qca8084_xpcs_set_mode(xpcs_mdiodev);
++}
+--- a/drivers/net/phy/qcom/qca8084_serdes.h
++++ b/drivers/net/phy/qcom/qca8084_serdes.h
+@@ -15,4 +15,6 @@ struct mdio_device *qca8084_package_pcs_
+ struct mdio_device *qca8084_package_xpcs_probe(struct device_node *xpcs_np);
+ void qca8084_package_xpcs_and_pcs_remove(struct mdio_device *xpcs_mdiodev,
+ struct mdio_device *pcs_mdiodev);
++int qca8084_qxgmii_set_mode(struct mdio_device *xpcs_mdiodev,
++ struct mdio_device *pcs_mdiodev);
+ #endif /* _QCA8084_SERDES_H_ */
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -926,6 +926,14 @@ static int qca8084_phy_package_config_in
+
+ usleep_range(10000, 11000);
+
++ /* Configure PCS working on 10G-QXGMII mode */
++ if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII) {
++ ret = qca8084_qxgmii_set_mode(shared_priv->mdiodev[1],
++ shared_priv->mdiodev[0]);
++ if (ret)
++ return ret;
++ }
++
+ /* Initialize the PHY package clock and reset, which is the
+ * necessary config sequence after GPIO reset on the PHY package.
+ */
+@@ -1164,6 +1172,9 @@ static int qca8084_probe(struct phy_devi
+
+ static void qca8084_remove(struct phy_device *phydev)
+ {
++ if (phydev->interface != PHY_INTERFACE_MODE_10G_QXGMII)
++ return;
++
+ if (phy_package_remove_once(phydev))
+ qca8084_phy_package_remove_once(phydev);
+ }
--- /dev/null
+From 2f5b7e167d847a5b5b74a91f991d48635453c55f Mon Sep 17 00:00:00 2001
+Date: Mon, 23 Sep 2024 21:24:56 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 SerDes speed config
+
+When the link of PHY is changed, the XPCS channel needs to be
+configured to adapt the current link status.
+
+Change-Id: I50d8973691dff133fc6bec1e9a1043bb646811fc
+Alex G: Use phy_package_get_*() accessors
+---
+ drivers/net/phy/qcom/qca8084_serdes.c | 159 ++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qca8084_serdes.h | 3 +
+ drivers/net/phy/qcom/qca808x.c | 15 ++-
+ 3 files changed, 175 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca8084_serdes.c
++++ b/drivers/net/phy/qcom/qca8084_serdes.c
+@@ -4,6 +4,7 @@
+ */
+
+ #include <linux/clk.h>
++#include <linux/clk-provider.h>
+ #include <linux/dev_printk.h>
+ #include <linux/mdio.h>
+ #include <linux/of.h>
+@@ -55,6 +56,13 @@
+ #define BYPASS_TUNNING_IPG 0x189
+ #define BYPASS_TUNNING_IPG_MASK GENMASK(11, 0)
+
++#define QP_USXG_RESET 0x18c
++#define QP_USXG_SGMII_FUNC_RESET BIT(4)
++#define QP_USXG_P3_FUNC_RESET BIT(3)
++#define QP_USXG_P2_FUNC_RESET BIT(2)
++#define QP_USXG_P1_FUNC_RESET BIT(1)
++#define QP_USXG_P0_FUNC_RESET BIT(0)
++
+ /* MDIO_MMD_PCS register */
+ #define PCS_CONTROL2 0x7
+ #define PCS_TYPE_MASK GENMASK(3, 0)
+@@ -107,6 +115,9 @@
+ #define TX_CONFIG BIT(3)
+ #define AUTO_NEGOTIATION_CMPLT_INTR BIT(0)
+
++#define PCS_ERR_SEL 0x8002
++#define PCS_AN_COMPLETE BIT(0)
++
+ #define XAUI_CONTROL 0x8004
+ #define TX_IPG_CHECK_DISABLE BIT(0)
+
+@@ -621,3 +632,151 @@ int qca8084_qxgmii_set_mode(struct mdio_
+
+ return qca8084_xpcs_set_mode(xpcs_mdiodev);
+ }
++
++static int qca8084_pcs_ipg_tune_reset(struct mdio_device *mdio_dev,
++ int reset_function)
++{
++ int ret;
++
++ ret = mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, QP_USXG_RESET,
++ reset_function, 0);
++ if (ret)
++ return ret;
++
++ usleep_range(1000, 1100);
++
++ return mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, QP_USXG_RESET,
++ reset_function, reset_function);
++}
++
++static int qca8084_xpcs_an_restart(struct mdio_device *xpcs_mdiodev,
++ int channel)
++{
++ int ret, mmd;
++
++ mmd = qca8084_xpcs_ch_mmd[channel];
++
++ /* Restart auto-negotiation */
++ ret = mdiodev_c45_modify(xpcs_mdiodev, mmd, MII_CONTROL,
++ AUTO_NEGOTIATION_RESTART,
++ AUTO_NEGOTIATION_RESTART);
++ if (ret)
++ return ret;
++
++ usleep_range(1000, 1100);
++
++ /* Clear pcs auto-negotiation complete interrupt */
++ return mdiodev_c45_modify(xpcs_mdiodev, mmd, PCS_ERR_SEL,
++ PCS_AN_COMPLETE, 0);
++}
++
++void qca8084_qxgmii_set_speed(struct mdio_device *xpcs_mdiodev,
++ struct mdio_device *pcs_mdiodev,
++ int channel, int speed)
++{
++ struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
++ struct qca8084_xpcs_channel_priv *xpcs_ch;
++ int mmd, i, ret, xpcs_rate;
++ unsigned long rate;
++
++ for (i = 0; i < QCA8084_CHANNEL_MAX; i++) {
++ xpcs_ch = &(xpcs_data->xpcs_ch[channel]);
++ if (channel == xpcs_ch->ch_id)
++ break;
++ }
++
++ if (i == QCA8084_CHANNEL_MAX) {
++ dev_err(&xpcs_mdiodev->dev, "Invalid channel %d\n", channel);
++ return;
++ }
++
++ mmd = qca8084_xpcs_ch_mmd[channel];
++
++ ret = qca8084_xpcs_an_restart(xpcs_mdiodev, channel);
++ if (ret)
++ return;
++
++ switch (speed) {
++ case SPEED_2500:
++ rate = 312500000;
++ xpcs_rate = PCS_SPEED_2500;
++ break;
++ case SPEED_1000:
++ rate = 125000000;
++ xpcs_rate = PCS_SPEED_1000;
++ break;
++ case SPEED_100:
++ rate = 25000000;
++ xpcs_rate = PCS_SPEED_100;
++ break;
++ case SPEED_10:
++ default:
++ rate = 2500000;
++ xpcs_rate = PCS_SPEED_10;
++ break;
++ }
++
++ clk_set_rate(xpcs_ch->clks[XPCS_RX_CLK], rate);
++ clk_set_rate(xpcs_ch->clks[XPCS_TX_CLK], rate);
++
++ /* XGMII takes the different clock rate 78.125Mhz from XPCS clock
++ * when linked at 2500M.
++ */
++ if (speed == SPEED_2500)
++ rate = 78125000;
++
++ clk_set_rate(xpcs_ch->clks[XPCS_XGMII_RX_CLK], rate);
++ clk_set_rate(xpcs_ch->clks[XPCS_XGMII_TX_CLK], rate);
++
++ ret = mdiodev_c45_modify(xpcs_mdiodev, mmd, MII_CONTROL,
++ PCS_SPEED_2500 | PCS_SPEED_1000 |
++ PCS_SPEED_100 | PCS_SPEED_10,
++ xpcs_rate);
++ if (ret)
++ return;
++
++ /* Disable clocks if link down with unknown speed. The channel clocks
++ * are disabled by default, __clk_is_enabled() is used to avoid
++ * disabling the clocks that is already in the disabled status.
++ */
++ if (speed == SPEED_UNKNOWN) {
++ if (__clk_is_enabled(xpcs_ch->clks[XPCS_RX_CLK]))
++ clk_disable_unprepare(xpcs_ch->clks[XPCS_RX_CLK]);
++ if (__clk_is_enabled(xpcs_ch->clks[XPCS_TX_CLK]))
++ clk_disable_unprepare(xpcs_ch->clks[XPCS_TX_CLK]);
++ if (__clk_is_enabled(xpcs_ch->clks[XPCS_PORT_RX_CLK]))
++ clk_disable_unprepare(xpcs_ch->clks[XPCS_PORT_RX_CLK]);
++ if (__clk_is_enabled(xpcs_ch->clks[XPCS_PORT_TX_CLK]))
++ clk_disable_unprepare(xpcs_ch->clks[XPCS_PORT_TX_CLK]);
++ if (__clk_is_enabled(xpcs_ch->clks[XPCS_XGMII_RX_CLK]))
++ clk_disable_unprepare(xpcs_ch->clks[XPCS_XGMII_RX_CLK]);
++ if (__clk_is_enabled(xpcs_ch->clks[XPCS_XGMII_TX_CLK]))
++ clk_disable_unprepare(xpcs_ch->clks[XPCS_XGMII_TX_CLK]);
++ } else {
++ clk_prepare_enable(xpcs_ch->clks[XPCS_RX_CLK]);
++ clk_prepare_enable(xpcs_ch->clks[XPCS_TX_CLK]);
++ clk_prepare_enable(xpcs_ch->clks[XPCS_PORT_RX_CLK]);
++ clk_prepare_enable(xpcs_ch->clks[XPCS_PORT_TX_CLK]);
++ clk_prepare_enable(xpcs_ch->clks[XPCS_XGMII_RX_CLK]);
++ clk_prepare_enable(xpcs_ch->clks[XPCS_XGMII_TX_CLK]);
++ }
++
++ msleep(100);
++
++ ret = reset_control_reset(xpcs_ch->rstcs);
++ if (ret)
++ return;
++
++ /* Reset IPG tune of PCS device. */
++ ret = qca8084_pcs_ipg_tune_reset(pcs_mdiodev, BIT(channel));
++ if (ret)
++ return;
++
++ if (channel == 0)
++ mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, DIG_CTRL1,
++ FIFO_RESET_CH0, FIFO_RESET_CH0);
++ else
++ mdiodev_c45_modify(xpcs_mdiodev, mmd, DIG_CTRL1,
++ FIFO_RESET_CH1_CH2_CH3,
++ FIFO_RESET_CH1_CH2_CH3);
++}
+--- a/drivers/net/phy/qcom/qca8084_serdes.h
++++ b/drivers/net/phy/qcom/qca8084_serdes.h
+@@ -17,4 +17,7 @@ void qca8084_package_xpcs_and_pcs_remove
+ struct mdio_device *pcs_mdiodev);
+ int qca8084_qxgmii_set_mode(struct mdio_device *xpcs_mdiodev,
+ struct mdio_device *pcs_mdiodev);
++void qca8084_qxgmii_set_speed(struct mdio_device *xpcs_mdiodev,
++ struct mdio_device *pcs_mdiodev,
++ int channel, int speed);
+ #endif /* _QCA8084_SERDES_H_ */
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -976,6 +976,7 @@ static int qca8084_config_init(struct ph
+
+ static void qca8084_link_change_notify(struct phy_device *phydev)
+ {
++ struct qca808x_shared_priv *shared_priv;
+ int ret;
+
+ /* Assert the FIFO between PHY and MAC. */
+@@ -1007,14 +1008,24 @@ static void qca8084_link_change_notify(s
+ }
+ }
+
+- /* Enable IPG level 10 to 11 tuning for link speed 1000M in the
++ /* Enable IPG level 10 to 11 tuning for link speed 1000M and
++ * configure the related XPCS channel with the phydev in the
+ * 10G_QXGMII mode.
+ */
+- if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
++ if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII) {
++ shared_priv = phy_package_get_priv(phydev);
++ struct qca808x_priv *priv = phydev->priv;
++
+ 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);
++
++ qca8084_qxgmii_set_speed(shared_priv->mdiodev[1],
++ shared_priv->mdiodev[0],
++ priv->channel_id,
++ phydev->speed);
++ }
+ }
+
+ /* QCA8084 is a four-port PHY, which integrates the clock controller,