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 index 0000000000..6fa5e4e23c --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0023-v6.15-net-phy-move-PHY-package-code-from-phy_device..patch @@ -0,0 +1,524 @@ +From 986ec7ee75f2f1f7f93eb0e05f61f297f6123fce Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +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 +Link: https://patch.msgid.link/57df5c19-fbcd-45a7-9afd-cd4f74d7fa76@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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 ++#include ++ ++/** ++ * 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 index 0000000000..a05083d6fe --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0024-v6.15-net-phy-add-getters-for-public-members-in-stru.patch @@ -0,0 +1,60 @@ +From e9ee1dd2ff2c130b2fb2bd01936224a8a6b49a7e Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +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 +Link: https://patch.msgid.link/c6da0b27-4479-4717-9e16-643821b76bd8@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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 + #include + ++#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 */ diff --git a/target/linux/qualcommbe/patches-6.12/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch b/target/linux/qualcommbe/patches-6.12/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch index 1583258493..a9223c5a35 100644 --- a/target/linux/qualcommbe/patches-6.12/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch +++ b/target/linux/qualcommbe/patches-6.12/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch @@ -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 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 +Alex G: Update to match the patches that will be upstream. +Signed-off-by: Alexandru Gagniuc --- - .../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 + - Luo Jie + +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 + | | | + Ref 50M clk +--------+ | | + ------------>| | clk & rst | | -+ GPIO Reset |QCA8K_CC+------------+ | ++ GPIO Reset |QCA8K-CC+------------+ | + ------------>| | | | + +--------+ | | + | V | @@ -71,17 +73,15 @@ Signed-off-by: Luo Jie + | 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 + - 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 + 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 + - clock-names + - resets + ++allOf: ++ - $ref: ethernet-phy-package.yaml# ++ +unevaluatedProperties: false + +examples: @@ -193,7 +335,7 @@ Signed-off-by: Luo Jie + "cnoc_ahb", + "mdio_ahb"; + resets = <&qca8k_nsscc NSS_CC_GEPHY_FULL_ARES>; -+ qcom,package-mode = ; ++ qcom,package-mode = ; + qcom,phy-addr-fixup = <1 2 3 4 5 6 7>; + + ethernet-phy@1 { @@ -201,6 +343,7 @@ Signed-off-by: Luo Jie + 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 + 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 + 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 + 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"; ++ }; + }; + }; + }; diff --git a/target/linux/qualcommbe/patches-6.12/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch b/target/linux/qualcommbe/patches-6.12/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch index edfe8d1404..360517f9b5 100644 --- a/target/linux/qualcommbe/patches-6.12/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch +++ b/target/linux/qualcommbe/patches-6.12/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch @@ -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 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 +Alex G: Update to match the patches that will be upstream. +Signed-off-by: Alexandru Gagniuc --- - 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 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 } 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 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 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, diff --git a/target/linux/qualcommbe/patches-6.12/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch b/target/linux/qualcommbe/patches-6.12/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch index 196160188b..8ac94c84c7 100644 --- a/target/linux/qualcommbe/patches-6.12/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch +++ b/target/linux/qualcommbe/patches-6.12/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch @@ -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 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 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 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, diff --git a/target/linux/qualcommbe/patches-6.12/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch b/target/linux/qualcommbe/patches-6.12/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch index 429b5c0535..e00a582831 100644 --- a/target/linux/qualcommbe/patches-6.12/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch +++ b/target/linux/qualcommbe/patches-6.12/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch @@ -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 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 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 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, diff --git a/target/linux/qualcommbe/patches-6.12/0310-net-phy-qca808x-Add-register-access-support-routines.patch b/target/linux/qualcommbe/patches-6.12/0310-net-phy-qca808x-Add-register-access-support-routines.patch index 899352a116..c1673ae645 100644 --- a/target/linux/qualcommbe/patches-6.12/0310-net-phy-qca808x-Add-register-access-support-routines.patch +++ b/target/linux/qualcommbe/patches-6.12/0310-net-phy-qca808x-Add-register-access-support-routines.patch @@ -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 Date: Wed, 29 Nov 2023 15:21:22 +0800 Subject: [PATCH] net: phy: qca808x: Add register access support routines for diff --git a/target/linux/qualcommbe/patches-6.12/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch b/target/linux/qualcommbe/patches-6.12/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch index b8c5e9ee91..e74fc0b09f 100644 --- a/target/linux/qualcommbe/patches-6.12/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch +++ b/target/linux/qualcommbe/patches-6.12/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch @@ -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 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 ---- - drivers/net/phy/qcom/qca808x.c | 91 ++++++++++++++++++++++++++++++++++ - 1 file changed, 91 insertions(+) +Alex G: include + 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 +freckup c89414adf2ec7c + +Signed-off-by: Alexandru Gagniuc +--- + 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 #include +#include ++#include +#include ++#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 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 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, diff --git a/target/linux/qualcommbe/patches-6.12/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch b/target/linux/qualcommbe/patches-6.12/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch index b41e725d30..50346520b3 100644 --- a/target/linux/qualcommbe/patches-6.12/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch +++ b/target/linux/qualcommbe/patches-6.12/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch @@ -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 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 +Alex G: Use accessors for struct phy_package_shared + Update to match the patches that will be upstream. +Signed-off-by: Alexandru Gagniuc --- - 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 - #include - #include -+#include - - #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 + 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 + [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 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 /* 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 + 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"); diff --git a/target/linux/qualcommbe/patches-6.12/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch b/target/linux/qualcommbe/patches-6.12/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch index bb5d0728e4..5af66c290f 100644 --- a/target/linux/qualcommbe/patches-6.12/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch +++ b/target/linux/qualcommbe/patches-6.12/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch @@ -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 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 +Alex G: Use phy_package_get_*() accessors + Update to match the patches that will be upstream. +Signed-off-by: Alexandru Gagniuc --- - 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 #include #include #include -@@ -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 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 + 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 + 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 + 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 + 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 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 index 0000000000..e46c6042ae --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0370-net-phy-Add-phy_package_remove_once-helper.patch @@ -0,0 +1,36 @@ +From d11eba3e178a9d42a579c656b2c9b643f4ce3e1e Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +--- + 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 index 0000000000..a0c84bddeb --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0371-net-phy-qca808x-Add-QCA8084-SerDes-probe-and-remove-.patch @@ -0,0 +1,437 @@ +From c12b79af730116936504afe97234f9afb6ac8fc0 Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Alex G: Use phy_package_get_*() accessors +Signed-off-by: Alexandru Gagniuc +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++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 + + #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 index 0000000000..688d7ac474 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0372-net-phy-qca808x-Add-QCA8084-SerDes-init-function.patch @@ -0,0 +1,446 @@ +From d137b725f8f4a7d49a809dcd73c5b836495ec44d Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +--- + 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 index 0000000000..1244837fd5 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0373-net-phy-qca808x-Add-QCA8084-SerDes-speed-config.patch @@ -0,0 +1,251 @@ +From 2f5b7e167d847a5b5b74a91f991d48635453c55f Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Alex G: Use phy_package_get_*() accessors +Signed-off-by: Alexandru Gagniuc +--- + 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 ++#include + #include + #include + #include +@@ -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,