qualcommbe: support 10g-qxgmii in QCA8084 PHY driver
authorAlexandru Gagniuc <[email protected]>
Sun, 9 Nov 2025 23:36:33 +0000 (17:36 -0600)
committerRobert Marko <[email protected]>
Tue, 11 Nov 2025 16:21:54 +0000 (17:21 +0100)
The "old" QCA8084 PHY driver does not implement 10g-qxgmii support.
This is blocking several devices which use the QCA8084 form being
merged. Qualcomm has provided updated drivers for the MAC (ppe), PCS,
and PHY via github. We only need to update the PHY driver.

Update the QCA8084 PHY driver using the patches provided by Qualcomm.
Re-organize the patches so that the changes go into the existing
patches. The SERDES functionality is new, so it gets new patches. This
is sufficient to enable 10g-qxgmii on ipq95xx platforms.

Signed-off-by: Alexandru Gagniuc <[email protected]>
Link: https://github.com/openwrt/openwrt/pull/20721
Signed-off-by: Robert Marko <[email protected]>
14 files changed:
target/linux/qualcommbe/patches-6.12/0023-v6.15-net-phy-move-PHY-package-code-from-phy_device..patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0024-v6.15-net-phy-add-getters-for-public-members-in-stru.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch
target/linux/qualcommbe/patches-6.12/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch
target/linux/qualcommbe/patches-6.12/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch
target/linux/qualcommbe/patches-6.12/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch
target/linux/qualcommbe/patches-6.12/0310-net-phy-qca808x-Add-register-access-support-routines.patch
target/linux/qualcommbe/patches-6.12/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch
target/linux/qualcommbe/patches-6.12/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch
target/linux/qualcommbe/patches-6.12/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch
target/linux/qualcommbe/patches-6.12/0370-net-phy-Add-phy_package_remove_once-helper.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0371-net-phy-qca808x-Add-QCA8084-SerDes-probe-and-remove-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0372-net-phy-qca808x-Add-QCA8084-SerDes-init-function.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0373-net-phy-qca808x-Add-QCA8084-SerDes-speed-config.patch [new file with mode: 0644]

diff --git a/target/linux/qualcommbe/patches-6.12/0023-v6.15-net-phy-move-PHY-package-code-from-phy_device..patch b/target/linux/qualcommbe/patches-6.12/0023-v6.15-net-phy-move-PHY-package-code-from-phy_device..patch
new file mode 100644 (file)
index 0000000..6fa5e4e
--- /dev/null
@@ -0,0 +1,524 @@
+From 986ec7ee75f2f1f7f93eb0e05f61f297f6123fce Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <[email protected]>
+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.
+
+Signed-off-by: Heiner Kallweit <[email protected]>
+Link: https://patch.msgid.link/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ 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);
diff --git a/target/linux/qualcommbe/patches-6.12/0024-v6.15-net-phy-add-getters-for-public-members-in-stru.patch b/target/linux/qualcommbe/patches-6.12/0024-v6.15-net-phy-add-getters-for-public-members-in-stru.patch
new file mode 100644 (file)
index 0000000..a05083d
--- /dev/null
@@ -0,0 +1,60 @@
+From e9ee1dd2ff2c130b2fb2bd01936224a8a6b49a7e Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <[email protected]>
+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.
+
+Signed-off-by: Heiner Kallweit <[email protected]>
+Link: https://patch.msgid.link/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ 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 */
index 1583258493fb192d60cbd8b50a90e6520ece8c1d..a9223c5a3510c883787cf4372e45a1ff679a9207 100644 (file)
@@ -1,4 +1,4 @@
-From ae682f13d308682232069e5150e884fc10160598 Mon Sep 17 00:00:00 2001
+From 7b1c4e22532ded6b20ee41936fa38b5ca1e61ff9 Mon Sep 17 00:00:00 2001
 From: Luo Jie <[email protected]>
 Date: Mon, 29 Jan 2024 17:57:20 +0800
 Subject: [PATCH] dt-bindings: net: Document Qualcomm QCA8084 PHY package
@@ -17,16 +17,18 @@ also be integrated to the switch chip named as QCA8386.
 
 Change-Id: Idb2338d2673152cbd3c57e95968faa59e9d4a80f
 Signed-off-by: Luo Jie <[email protected]>
+Alex G: Update to match the patches that will be upstream.
+Signed-off-by: Alexandru Gagniuc <[email protected]>
 ---
- .../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
 +---
@@ -39,8 +41,8 @@ Signed-off-by: Luo Jie <[email protected]>
 +  - Luo Jie <[email protected]>
 +
 +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
@@ -63,7 +65,7 @@ Signed-off-by: Luo Jie <[email protected]>
 +                 |                     |             |
 +    Ref 50M clk  +--------+            |             |
 +    ------------>|        | clk & rst  |             |
-+    GPIO Reset   |QCA8K_CC+------------+             |
++    GPIO Reset   |QCA8K-CC+------------+             |
 +    ------------>|        |            |             |
 +                 +--------+            |             |
 +                 |                     V             |
@@ -71,17 +73,15 @@ Signed-off-by: Luo Jie <[email protected]>
 +                 |  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
@@ -102,10 +102,11 @@ Signed-off-by: Luo Jie <[email protected]>
 +      - 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:
@@ -130,30 +131,168 @@ Signed-off-by: Luo Jie <[email protected]>
 +    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
@@ -161,6 +300,9 @@ Signed-off-by: Luo Jie <[email protected]>
 +  - clock-names
 +  - resets
 +
++allOf:
++  - $ref: ethernet-phy-package.yaml#
++
 +unevaluatedProperties: false
 +
 +examples:
@@ -193,7 +335,7 @@ Signed-off-by: Luo Jie <[email protected]>
 +                          "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 {
@@ -201,6 +343,7 @@ Signed-off-by: Luo Jie <[email protected]>
 +                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 {
@@ -208,6 +351,7 @@ Signed-off-by: Luo Jie <[email protected]>
 +                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 {
@@ -215,6 +359,7 @@ Signed-off-by: Luo Jie <[email protected]>
 +                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 {
@@ -222,6 +367,153 @@ Signed-off-by: Luo Jie <[email protected]>
 +                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";
++                };
 +            };
 +        };
 +    };
index edfe8d1404423dfd34ecd2cb9d43a4e64ffb0554..360517f9b582b98e9d77c13e75371e32a19d2d66 100644 (file)
@@ -1,4 +1,4 @@
-From 816bff9bcd2ff7c1e84dd14fc81c9c1bdaa609e7 Mon Sep 17 00:00:00 2001
+From 60c44842f9611be237ab3f68afe8ebf2d9595fb2 Mon Sep 17 00:00:00 2001
 From: Luo Jie <[email protected]>
 Date: Thu, 6 Apr 2023 18:09:07 +0800
 Subject: [PATCH] net: phy: qca808x: Add QCA8084 ethernet phy support
@@ -20,9 +20,11 @@ with QCA8081.
 
 Change-Id: I12555fa70662682474ab4432204405b5e752fef6
 Signed-off-by: Luo Jie <[email protected]>
+Alex G: Update to match the patches that will be upstream.
+Signed-off-by: Alexandru Gagniuc <[email protected]>
 ---
- 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
@@ -44,7 +46,7 @@ Signed-off-by: Luo Jie <[email protected]>
  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)
  {
@@ -55,7 +57,16 @@ Signed-off-by: Luo Jie <[email protected]>
  }
  
  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) {
@@ -79,7 +90,7 @@ Signed-off-by: Luo Jie <[email protected]>
                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);
  
@@ -98,7 +109,7 @@ Signed-off-by: Luo Jie <[email protected]>
        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,
index 196160188b11e3c34f5994cc1fba32cf87d7757b..8ac94c84c75ba485fa6af136793dc7ef3656cc37 100644 (file)
@@ -1,4 +1,4 @@
-From 5a57611512593212b7fd9c23b4d96486bab6dee3 Mon Sep 17 00:00:00 2001
+From c052b9a4ab869cc54976402b3f9dbdef5bdb9f27 Mon Sep 17 00:00:00 2001
 From: Luo Jie <[email protected]>
 Date: Wed, 8 Nov 2023 16:18:02 +0800
 Subject: [PATCH] net: phy: qca808x: Add config_init function for QCA8084
@@ -40,7 +40,7 @@ Signed-off-by: Luo Jie <[email protected]>
  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);
  }
  
@@ -75,7 +75,7 @@ Signed-off-by: Luo Jie <[email protected]>
  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,
index 429b5c0535b299545d34a4c2dd52f8607560c555..e00a5828312b8e1d4689678c31f5fd6aca2d8a82 100644 (file)
@@ -1,4 +1,4 @@
-From d1f2a1810af1833196934977f57607432fda46b4 Mon Sep 17 00:00:00 2001
+From aec49c172cd9c739c1d97ff2d42b9718bb20b609 Mon Sep 17 00:00:00 2001
 From: Luo Jie <[email protected]>
 Date: Wed, 8 Nov 2023 18:01:14 +0800
 Subject: [PATCH] net: phy: qca808x: Add link_change_notify function for
@@ -30,7 +30,7 @@ Signed-off-by: Luo Jie <[email protected]>
  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);
  }
  
@@ -80,7 +80,7 @@ Signed-off-by: Luo Jie <[email protected]>
  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,
index 899352a11689f7c6b6c17e33315d8916d781d512..c1673ae645ca5d2464f02413609190247fc7d944 100644 (file)
@@ -1,4 +1,4 @@
-From c17f19be3bec0bf5467f4e14a21573836910f671 Mon Sep 17 00:00:00 2001
+From cea8043def0c0867370c2efd5a1cd73bf4d3e5ba Mon Sep 17 00:00:00 2001
 From: Luo Jie <[email protected]>
 Date: Wed, 29 Nov 2023 15:21:22 +0800
 Subject: [PATCH] net: phy: qca808x: Add register access support routines for
index b8c5e9ee9138b7ff851b1df014c0dcf7e18b0989..e74fc0b09f902687d810e39ce8218b80df041f69 100644 (file)
@@ -1,4 +1,4 @@
-From 485f973c5b1d889bd1f48a188137d80d45004991 Mon Sep 17 00:00:00 2001
+From a7fe2c13f3188bf01b60fb15063d028c76dd2f1a Mon Sep 17 00:00:00 2001
 From: Luo Jie <[email protected]>
 Date: Mon, 29 Jan 2024 10:51:38 +0800
 Subject: [PATCH] net: phy: qca808x: Add QCA8084 probe function
@@ -13,22 +13,45 @@ accessed, and the features of PHY can be acquired.
 
 Change-Id: I2251b9c5c398a21a4ef547a727189a934ad3a44c
 Signed-off-by: Luo Jie <[email protected]>
+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
+Signed-off-by: Alexandru Gagniuc <[email protected]>
+
+freckup c89414adf2ec7c
+
+Signed-off-by: Alexandru Gagniuc <[email protected]>
 ---
- 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)
  
@@ -50,21 +73,20 @@ Signed-off-by: Luo Jie <[email protected]>
  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;
@@ -130,7 +152,7 @@ Signed-off-by: Luo Jie <[email protected]>
  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,
index b41e725d3065b4fa600f1c7ebd20b92b221534ce..50346520b31a626eb3b5f20d4134c5f56d7e01af 100644 (file)
@@ -1,4 +1,4 @@
-From 685566f8b765f522b7f4d4deb06bf84a557dc4ac Mon Sep 17 00:00:00 2001
+From 57379fe257895b374d35ce6578ecd62ce1cc1a4d Mon Sep 17 00:00:00 2001
 From: Luo Jie <[email protected]>
 Date: Tue, 9 Apr 2024 16:30:55 +0800
 Subject: [PATCH] net: phy: qca808x: Add package clocks and resets for QCA8084
@@ -12,21 +12,16 @@ the PHY registers.
 
 Change-Id: I254d0aa0a1155d3618c6f1fc7d7a5b6ecadccbaa
 Signed-off-by: Luo Jie <[email protected]>
+Alex G: Use accessors for struct phy_package_shared
+        Update to match the patches that will be upstream.
+Signed-off-by: Alexandru Gagniuc <[email protected]>
 ---
- 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");
  
@@ -38,6 +33,8 @@ Signed-off-by: Luo Jie <[email protected]>
 +      TLMM_AHB_CLK,
 +      CNOC_AHB_CLK,
 +      MDIO_AHB_CLK,
++      MDIO_MASTER_AHB_CLK,
++      SWITCH_CORE_CLK,
 +      PACKAGE_CLK_MAX
 +};
 +
@@ -57,12 +54,14 @@ Signed-off-by: Luo Jie <[email protected]>
 +      [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);
  }
  
@@ -79,7 +78,7 @@ Signed-off-by: Luo Jie <[email protected]>
  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;
@@ -88,7 +87,7 @@ Signed-off-by: Luo Jie <[email protected]>
  
        /* 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]);
  
@@ -97,18 +96,22 @@ Signed-off-by: Luo Jie <[email protected]>
 +      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");
index bb5d0728e41fab5e25b4f4e8bf62d2590c8c5073..5af66c290fc79e24b7a7c4790471fee0f12b1238 100644 (file)
@@ -1,4 +1,4 @@
-From bf779b10b00fd79267d0ef625ae246df59ee23bd Mon Sep 17 00:00:00 2001
+From d39dc53424bcc778f1e468015490577e7bf0c7b6 Mon Sep 17 00:00:00 2001
 From: Luo Jie <[email protected]>
 Date: Thu, 25 Jan 2024 17:13:24 +0800
 Subject: [PATCH] net: phy: qca808x: Add QCA8084 package init function
@@ -10,9 +10,12 @@ The PHY package level clocks are enabled and their rates configured.
 
 Change-Id: I63d4b22d2a70ee713cc6a6818b0f3c7aa098a5f5
 Signed-off-by: Luo Jie <[email protected]>
+Alex G: Use phy_package_get_*() accessors
+        Update to match the patches that will be upstream.
+Signed-off-by: Alexandru Gagniuc <[email protected]>
 ---
- 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
@@ -23,21 +26,20 @@ Signed-off-by: Luo Jie <[email protected]>
  #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 {
@@ -45,7 +47,7 @@ Signed-off-by: Luo Jie <[email protected]>
        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);
  }
  
@@ -60,6 +62,10 @@ Signed-off-by: Luo Jie <[email protected]>
 +      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;
@@ -91,16 +97,19 @@ Signed-off-by: Luo Jie <[email protected]>
 +      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;
@@ -123,13 +132,6 @@ Signed-off-by: Luo Jie <[email protected]>
 +      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);
@@ -137,7 +139,11 @@ Signed-off-by: Luo Jie <[email protected]>
 +              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)
@@ -153,19 +159,19 @@ Signed-off-by: Luo Jie <[email protected]>
        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),
diff --git a/target/linux/qualcommbe/patches-6.12/0370-net-phy-Add-phy_package_remove_once-helper.patch b/target/linux/qualcommbe/patches-6.12/0370-net-phy-Add-phy_package_remove_once-helper.patch
new file mode 100644 (file)
index 0000000..e46c604
--- /dev/null
@@ -0,0 +1,36 @@
+From d11eba3e178a9d42a579c656b2c9b643f4ce3e1e Mon Sep 17 00:00:00 2001
+From: Luo Jie <[email protected]>
+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
+Signed-off-by: Luo Jie <[email protected]>
+---
+ 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 {
diff --git a/target/linux/qualcommbe/patches-6.12/0371-net-phy-qca808x-Add-QCA8084-SerDes-probe-and-remove-.patch b/target/linux/qualcommbe/patches-6.12/0371-net-phy-qca808x-Add-QCA8084-SerDes-probe-and-remove-.patch
new file mode 100644 (file)
index 0000000..a0c84bd
--- /dev/null
@@ -0,0 +1,437 @@
+From c12b79af730116936504afe97234f9afb6ac8fc0 Mon Sep 17 00:00:00 2001
+From: Luo Jie <[email protected]>
+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
+Signed-off-by: Luo Jie <[email protected]>
+Alex G: Use phy_package_get_*() accessors
+Signed-off-by: Alexandru Gagniuc <[email protected]>
+---
+ 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);
diff --git a/target/linux/qualcommbe/patches-6.12/0372-net-phy-qca808x-Add-QCA8084-SerDes-init-function.patch b/target/linux/qualcommbe/patches-6.12/0372-net-phy-qca808x-Add-QCA8084-SerDes-init-function.patch
new file mode 100644 (file)
index 0000000..688d7ac
--- /dev/null
@@ -0,0 +1,446 @@
+From d137b725f8f4a7d49a809dcd73c5b836495ec44d Mon Sep 17 00:00:00 2001
+From: Luo Jie <[email protected]>
+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
+Signed-off-by: Luo Jie <[email protected]>
+---
+ 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);
+ }
diff --git a/target/linux/qualcommbe/patches-6.12/0373-net-phy-qca808x-Add-QCA8084-SerDes-speed-config.patch b/target/linux/qualcommbe/patches-6.12/0373-net-phy-qca808x-Add-QCA8084-SerDes-speed-config.patch
new file mode 100644 (file)
index 0000000..1244837
--- /dev/null
@@ -0,0 +1,251 @@
+From 2f5b7e167d847a5b5b74a91f991d48635453c55f Mon Sep 17 00:00:00 2001
+From: Luo Jie <[email protected]>
+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
+Signed-off-by: Luo Jie <[email protected]>
+Alex G: Use phy_package_get_*() accessors
+Signed-off-by: Alexandru Gagniuc <[email protected]>
+---
+ 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,