* [PATCH 0/3] phy: core: Add phy bulk helpers support
@ 2026-08-31 2:53 Inochi Amaoto
2026-08-31 2:55 ` [PATCH 1/3] phy: core: Add common helper to add phy phandle device link Inochi Amaoto
0 siblings, 1 reply; 10+ messages in thread
From: Inochi Amaoto @ 2026-08-31 2:53 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam
Cc: Andy Shevchenko, Inochi Amaoto, linux-phy, linux-kernel,
Yixun Lan, Longbin Li
As discussed in [1], some drivers may do not care the difference between
phys. Instead, they only need to treat them as a group and operate them
together. This means a bulk operation is needed.
Add some bulk helper functions for phy core by referencing the design of
clock/reset subsystem. This can relieve the driver owners' life who needs
to handle many phys, as well as each phy error reporting.
[1] https://lore.kernel.org/linux-pci/ak9KzNFF26B0Kttz@ashevche-desk.local/
Inochi Amaoto (3):
phy: core: Add common helper to add phy phandle device link
phy: core: Add common helper for get phy phandle by index
phy: core: Add phy bulk data helper functions
drivers/phy/phy-core.c | 716 ++++++++++++++++++++++++++++++++++++++--
include/linux/phy/phy.h | 183 ++++++++++
2 files changed, 865 insertions(+), 34 deletions(-)
--
2.55.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/3] phy: core: Add common helper to add phy phandle device link
2026-08-31 2:53 [PATCH 0/3] phy: core: Add phy bulk helpers support Inochi Amaoto
@ 2026-08-31 2:55 ` Inochi Amaoto
2026-08-31 2:55 ` [PATCH 2/3] phy: core: Add common helper for get phy phandle by index Inochi Amaoto
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Inochi Amaoto @ 2026-08-31 2:55 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam
Cc: Andy Shevchenko, Inochi Amaoto, linux-phy, linux-kernel,
Yixun Lan, Longbin Li
It is very common for adding a device link for phy phandle
for device managed phy helper functions. So add a common
helper for future reuse.
Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
---
drivers/phy/phy-core.c | 38 +++++++++++++++++++++++---------------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 21aaf2f76e53..451f8f0644c1 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -606,6 +606,26 @@ int phy_validate(struct phy *phy, enum phy_mode mode, int submode,
}
EXPORT_SYMBOL_GPL(phy_validate);
+/**
+ * phy_add_device_link() - Link the phy phandle to the device
+ * @dev: the device to link the phy phandle
+ * @phy: the phy phandle to link
+ *
+ * Link the phy phandle to the device by using device link.
+ */
+static void phy_add_device_link(struct device *dev, struct phy *phy)
+{
+ struct device_link *link;
+
+ if (!phy)
+ return;
+
+ link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
+ if (!link)
+ dev_dbg(dev, "failed to create device link to %s\n",
+ dev_name(phy->dev.parent));
+}
+
/**
* _of_phy_get() - lookup and obtain a reference to a phy by phandle
* @np: device_node for which to get the phy
@@ -784,7 +804,6 @@ struct phy *phy_get(struct device *dev, const char *string)
{
int index = 0;
struct phy *phy;
- struct device_link *link;
if (dev->of_node) {
if (string)
@@ -808,10 +827,7 @@ struct phy *phy_get(struct device *dev, const char *string)
get_device(&phy->dev);
- link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
- if (!link)
- dev_dbg(dev, "failed to create device link to %s\n",
- dev_name(phy->dev.parent));
+ phy_add_device_link(dev, phy);
return phy;
}
@@ -885,7 +901,6 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
const char *con_id)
{
struct phy **ptr, *phy;
- struct device_link *link;
ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
@@ -900,10 +915,7 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
return phy;
}
- link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
- if (!link)
- dev_dbg(dev, "failed to create device link to %s\n",
- dev_name(phy->dev.parent));
+ phy_add_device_link(dev, phy);
return phy;
}
@@ -955,7 +967,6 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
int index)
{
struct phy **ptr, *phy;
- struct device_link *link;
ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
@@ -977,10 +988,7 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
*ptr = phy;
devres_add(dev, ptr);
- link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
- if (!link)
- dev_dbg(dev, "failed to create device link to %s\n",
- dev_name(phy->dev.parent));
+ phy_add_device_link(dev, phy);
return phy;
}
--
2.55.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/3] phy: core: Add common helper for get phy phandle by index
2026-08-31 2:55 ` [PATCH 1/3] phy: core: Add common helper to add phy phandle device link Inochi Amaoto
@ 2026-08-31 2:55 ` Inochi Amaoto
2026-08-31 3:03 ` sashiko-bot
2026-08-31 9:13 ` Andy Shevchenko
2026-08-31 2:55 ` [PATCH 3/3] phy: core: Add phy bulk data helper functions Inochi Amaoto
2026-08-31 3:03 ` [PATCH 1/3] phy: core: Add common helper to add phy phandle device link sashiko-bot
2 siblings, 2 replies; 10+ messages in thread
From: Inochi Amaoto @ 2026-08-31 2:55 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam
Cc: Andy Shevchenko, Inochi Amaoto, linux-phy, linux-kernel,
Yixun Lan, Longbin Li
Several phy helper use index to get phy phandle of a device node,
add a common function for the future reuse.
Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
---
drivers/phy/phy-core.c | 47 +++++++++++++++++++++++++-----------------
1 file changed, 28 insertions(+), 19 deletions(-)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 451f8f0644c1..2259f0dff082 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -681,6 +681,32 @@ static struct phy *_of_phy_get(struct device_node *np, int index)
return phy;
}
+/**
+ * of_phy_get_by_index() - lookup and obtain a reference to a phy using a
+ * device_node by index.
+ * @np: device_node for which to get the phy
+ * @index: index of the phy from device's point of view
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy. The caller is responsible for
+ * calling of_phy_put() to release that count.
+ */
+static struct phy *of_phy_get_by_index(struct device_node *np, int index)
+{
+ struct phy *phy;
+
+ phy = _of_phy_get(np, index);
+ if (IS_ERR(phy))
+ return phy;
+
+ if (!try_module_get(phy->ops->owner))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ get_device(&phy->dev);
+
+ return phy;
+}
+
/**
* of_phy_get() - lookup and obtain a reference to a phy using a device_node.
* @np: device_node for which to get the phy
@@ -692,22 +718,12 @@ static struct phy *_of_phy_get(struct device_node *np, int index)
*/
struct phy *of_phy_get(struct device_node *np, const char *con_id)
{
- struct phy *phy = NULL;
int index = 0;
if (con_id)
index = of_property_match_string(np, "phy-names", con_id);
- phy = _of_phy_get(np, index);
- if (IS_ERR(phy))
- return phy;
-
- if (!try_module_get(phy->ops->owner))
- return ERR_PTR(-EPROBE_DEFER);
-
- get_device(&phy->dev);
-
- return phy;
+ return of_phy_get_by_index(np, index);
}
EXPORT_SYMBOL_GPL(of_phy_get);
@@ -972,19 +988,12 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
if (!ptr)
return ERR_PTR(-ENOMEM);
- phy = _of_phy_get(np, index);
+ phy = of_phy_get_by_index(np, index);
if (IS_ERR(phy)) {
devres_free(ptr);
return phy;
}
- if (!try_module_get(phy->ops->owner)) {
- devres_free(ptr);
- return ERR_PTR(-EPROBE_DEFER);
- }
-
- get_device(&phy->dev);
-
*ptr = phy;
devres_add(dev, ptr);
--
2.55.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/3] phy: core: Add phy bulk data helper functions
2026-08-31 2:55 ` [PATCH 1/3] phy: core: Add common helper to add phy phandle device link Inochi Amaoto
2026-08-31 2:55 ` [PATCH 2/3] phy: core: Add common helper for get phy phandle by index Inochi Amaoto
@ 2026-08-31 2:55 ` Inochi Amaoto
2026-08-31 9:28 ` Andy Shevchenko
2026-08-31 3:03 ` [PATCH 1/3] phy: core: Add common helper to add phy phandle device link sashiko-bot
2 siblings, 1 reply; 10+ messages in thread
From: Inochi Amaoto @ 2026-08-31 2:55 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam
Cc: Andy Shevchenko, Inochi Amaoto, linux-phy, linux-kernel,
Yixun Lan, Longbin Li
Add several helper functions that allow drivers to get several phy
consumers in one operation. If any of the phy cannot be acquired then
any phys that were got will be put before returning to the caller.
This can relieve the driver owners' life who needs to handle many phys,
as well as each phy error reporting.
Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
---
drivers/phy/phy-core.c | 631 ++++++++++++++++++++++++++++++++++++++++
include/linux/phy/phy.h | 183 ++++++++++++
2 files changed, 814 insertions(+)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 2259f0dff082..26b48cece312 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -1003,6 +1003,637 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
}
EXPORT_SYMBOL_GPL(devm_of_phy_get_by_index);
+/**
+ * of_phy_get_parent_count() - Get the number of phys of a device node
+ * @np: device_node for which to get the phy
+ *
+ * Return: the phy count if successful, 0 if no phy handle is found,
+ * negative error value if error occurs.
+ */
+static int of_phy_get_parent_count(const struct device_node *np)
+{
+ int count;
+
+ count = of_count_phandle_with_args(np, "phys", "#phy-cells");
+
+ if (count == -ENOENT)
+ return 0;
+
+ return count;
+}
+
+/**
+ * phy_bulk_put() - release a set of PHYs obtained with phy_bulk_get()
+ * @dev: device that acquired the PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data with PHYs set
+ *
+ * Releases the PHY references in reverse order and clears the PHY pointer in
+ * each entry. The caller owns the phys array and is responsible for freeing it
+ * if necessary.
+ */
+void phy_bulk_put(struct device *dev, int num_phys, struct phy_bulk_data *phys)
+{
+ if (!phys)
+ return;
+
+ while (--num_phys >= 0) {
+ if (phys[num_phys].phy)
+ phy_put(dev, phys[num_phys].phy);
+ phys[num_phys].phy = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(phy_bulk_put);
+
+/**
+ * of_phy_bulk_put() - release a set of PHYs obtained with of_phy_bulk_get()
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data with PHYs set
+ *
+ * Releases the PHY references in reverse order and clears the PHY pointer in
+ * each entry. The caller owns the phys array and is responsible for freeing it
+ * if necessary.
+ */
+void of_phy_bulk_put(int num_phys, struct phy_bulk_data *phys)
+{
+ if (!phys)
+ return;
+
+ while (--num_phys >= 0) {
+ of_phy_put(phys[num_phys].phy);
+ phys[num_phys].phy = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(of_phy_bulk_put);
+
+static int __phy_bulk_get(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys, bool optional)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < num_phys; i++)
+ phys[i].phy = NULL;
+
+ for (i = 0; i < num_phys; i++) {
+ phys[i].phy = phy_get(dev, phys[i].id);
+ if (IS_ERR(phys[i].phy)) {
+ ret = PTR_ERR(phys[i].phy);
+ phys[i].phy = NULL;
+
+ if (ret == -ENODEV && optional)
+ continue;
+
+ dev_err_probe(dev, ret,
+ "Failed to get phy: (%s)\n",
+ phys[i].id);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ phy_bulk_put(dev, i, phys);
+
+ return ret;
+}
+
+/**
+ * phy_bulk_get() - lookup and obtain references to multiple PHYs
+ * @dev: device that requests the PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data with PHY names set
+ *
+ * Gets each PHY using phy_get(). This supports both device tree lookups and
+ * non-device-tree lookups registered with phy_create_lookup(). The caller must
+ * call phy_bulk_put() to release the PHY references.
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
+int phy_bulk_get(struct device *dev, int num_phys, struct phy_bulk_data *phys)
+{
+ return __phy_bulk_get(dev, num_phys, phys, false);
+}
+EXPORT_SYMBOL_GPL(phy_bulk_get);
+
+/**
+ * phy_bulk_get_optional() - obtain references to multiple optional PHYs
+ * @dev: device that requests the PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data with PHY names set
+ *
+ * Gets each PHY using phy_get(). A PHY that is not present is stored as NULL
+ * instead of causing the operation to fail. The caller must call
+ * phy_bulk_put() to release the PHY references.
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
+int phy_bulk_get_optional(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ return __phy_bulk_get(dev, num_phys, phys, true);
+}
+EXPORT_SYMBOL_GPL(phy_bulk_get_optional);
+
+/**
+ * of_phy_bulk_get() - obtain references to multiple PHYs from a device node
+ * @np: device node containing the PHY references
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data with PHY names set
+ *
+ * Gets each PHY using of_phy_get() and the specified device node. The caller
+ * must call of_phy_bulk_put() to release the PHY references.
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
+int of_phy_bulk_get(struct device_node *np, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ int ret, i;
+
+ for (i = 0; i < num_phys; i++)
+ phys[i].phy = NULL;
+
+ for (i = 0; i < num_phys; i++) {
+ phys[i].phy = of_phy_get(np, phys[i].id);
+ if (IS_ERR(phys[i].phy)) {
+ ret = PTR_ERR(phys[i].phy);
+ phys[i].phy = NULL;
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ of_phy_bulk_put(i, phys);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_phy_bulk_get);
+
+static int of_phy_bulk_get_by_index(struct device_node *np, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ int ret, i;
+
+ for (i = 0; i < num_phys; i++) {
+ phys[i].id = NULL;
+ phys[i].phy = NULL;
+ }
+
+ for (i = 0; i < num_phys; i++) {
+ of_property_read_string_index(np, "phy-names", i,
+ &phys[i].id);
+
+ phys[i].phy = of_phy_get_by_index(np, i);
+ if (IS_ERR(phys[i].phy)) {
+ ret = PTR_ERR(phys[i].phy);
+ phys[i].phy = NULL;
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ of_phy_bulk_put(i, phys);
+
+ return ret;
+}
+
+/**
+ * of_phy_bulk_get_all() - obtain all PHYs from a device node
+ * @np: device node containing the PHY references
+ * @phys: pointer to store the allocated array of struct phy_bulk_data
+ *
+ * Gets every PHY referenced by the phys property in index order. PHY names are
+ * read from phy-names when present. The caller must call
+ * of_phy_bulk_put_all() to release the PHY references and free the array.
+ *
+ * Return: the number of PHYs on success, %0 if no PHYs are found, or a
+ * negative error code otherwise
+ */
+int of_phy_bulk_get_all(struct device_node *np, struct phy_bulk_data **phys)
+{
+ struct phy_bulk_data *phy_bulk;
+ int num_phys;
+ int ret;
+
+ *phys = NULL;
+
+ if (!np)
+ return 0;
+
+ num_phys = of_phy_get_parent_count(np);
+ if (num_phys <= 0)
+ return num_phys;
+
+ phy_bulk = kmalloc_objs(*phy_bulk, num_phys);
+ if (!phy_bulk)
+ return -ENOMEM;
+
+ ret = of_phy_bulk_get_by_index(np, num_phys, phy_bulk);
+ if (ret) {
+ kfree(phy_bulk);
+ return ret;
+ }
+
+ *phys = phy_bulk;
+
+ return num_phys;
+}
+EXPORT_SYMBOL_GPL(of_phy_bulk_get_all);
+
+/**
+ * of_phy_bulk_put_all() - release and free PHYs obtained by
+ * of_phy_bulk_get_all()
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data to release and free
+ */
+void of_phy_bulk_put_all(int num_phys, struct phy_bulk_data *phys)
+{
+ if (IS_ERR_OR_NULL(phys))
+ return;
+
+ of_phy_bulk_put(num_phys, phys);
+ kfree(phys);
+}
+EXPORT_SYMBOL_GPL(of_phy_bulk_put_all);
+
+/**
+ * phy_bulk_get_all() - obtain all PHYs requested by a device
+ * @dev: device that requests the PHYs
+ * @phys: pointer to store the allocated array of struct phy_bulk_data
+ *
+ * Gets every PHY referenced by the device's device tree node and creates a
+ * device link for each PHY. The caller must call phy_bulk_put_all() to release
+ * the PHY references and free the array.
+ *
+ * Return: the number of PHYs on success, %0 if no PHYs are found, or a
+ * negative error code otherwise
+ */
+int phy_bulk_get_all(struct device *dev, struct phy_bulk_data **phys)
+{
+ struct device_node *np = dev_of_node(dev);
+ int ret, i;
+
+ *phys = NULL;
+
+ if (!np)
+ return 0;
+
+ ret = of_phy_bulk_get_all(np, phys);
+ if (ret > 0)
+ for (i = 0; i < ret; i++)
+ phy_add_device_link(dev, (*phys)[i].phy);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_bulk_get_all);
+
+/**
+ * phy_bulk_put_all() - release and free PHYs obtained by phy_bulk_get_all()
+ * @dev: device that acquired the PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data to release and free
+ */
+void phy_bulk_put_all(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ if (IS_ERR_OR_NULL(phys))
+ return;
+
+ phy_bulk_put(dev, num_phys, phys);
+ kfree(phys);
+}
+EXPORT_SYMBOL_GPL(phy_bulk_put_all);
+
+struct phy_bulk_devres {
+ struct phy_bulk_data *phys;
+ int num_phys;
+ bool free_phys;
+};
+
+static void devm_phy_bulk_release(struct device *dev, void *res)
+{
+ struct phy_bulk_devres *devres = res;
+
+ if (devres->free_phys)
+ phy_bulk_put_all(dev, devres->num_phys, devres->phys);
+ else
+ phy_bulk_put(dev, devres->num_phys, devres->phys);
+}
+
+static int __devm_phy_bulk_get(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys, bool optional)
+{
+ struct phy_bulk_devres *devres;
+ int ret;
+
+ devres = devres_alloc(devm_phy_bulk_release, sizeof(*devres),
+ GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ if (optional)
+ ret = phy_bulk_get_optional(dev, num_phys, phys);
+ else
+ ret = phy_bulk_get(dev, num_phys, phys);
+ if (ret) {
+ devres_free(devres);
+ return ret;
+ }
+
+ devres->phys = phys;
+ devres->num_phys = num_phys;
+ devres->free_phys = false;
+ devres_add(dev, devres);
+
+ return 0;
+}
+
+/**
+ * devm_phy_bulk_get() - managed lookup of multiple PHYs
+ * @dev: device that requests the PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data with PHY names set
+ *
+ * Gets the PHYs using phy_bulk_get() and associates the references with @dev.
+ * The references are automatically released on driver detach.
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
+int devm_phy_bulk_get(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ return __devm_phy_bulk_get(dev, num_phys, phys, false);
+}
+EXPORT_SYMBOL_GPL(devm_phy_bulk_get);
+
+/**
+ * devm_phy_bulk_get_optional() - managed lookup of multiple optional PHYs
+ * @dev: device that requests the PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data with PHY names set
+ *
+ * Gets the PHYs using phy_bulk_get_optional() and associates the references
+ * with @dev. Missing PHYs are stored as NULL. The references are automatically
+ * released on driver detach.
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
+int devm_phy_bulk_get_optional(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ return __devm_phy_bulk_get(dev, num_phys, phys, true);
+}
+EXPORT_SYMBOL_GPL(devm_phy_bulk_get_optional);
+
+/**
+ * devm_of_phy_bulk_get() - managed lookup of multiple PHYs from a device node
+ * @dev: device that requests the PHYs
+ * @np: device node containing the PHY references
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data with PHY names set
+ *
+ * Gets the PHYs using of_phy_bulk_get() from the specified device node,
+ * associates the references with @dev, and creates a device link for each PHY.
+ * The references are automatically released on driver detach.
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
+int devm_of_phy_bulk_get(struct device *dev, struct device_node *np,
+ int num_phys, struct phy_bulk_data *phys)
+{
+ struct phy_bulk_devres *devres;
+ int ret, i;
+
+ devres = devres_alloc(devm_phy_bulk_release, sizeof(*devres),
+ GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ ret = of_phy_bulk_get(np, num_phys, phys);
+ if (ret) {
+ devres_free(devres);
+ return ret;
+ }
+
+ for (i = 0; i < num_phys; i++)
+ phy_add_device_link(dev, phys[i].phy);
+
+ devres->phys = phys;
+ devres->num_phys = num_phys;
+ devres->free_phys = false;
+ devres_add(dev, devres);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_bulk_get);
+
+/**
+ * devm_phy_bulk_get_all() - managed lookup of all PHYs requested by a device
+ * @dev: device that requests the PHYs
+ * @phys: pointer to store the allocated array of struct phy_bulk_data
+ *
+ * Gets all PHYs using phy_bulk_get_all() and associates the allocated array and
+ * PHY references with @dev. They are automatically released on driver detach.
+ *
+ * Return: the number of PHYs on success, %0 if no PHYs are found, or a
+ * negative error code otherwise
+ */
+int devm_phy_bulk_get_all(struct device *dev, struct phy_bulk_data **phys)
+{
+ struct phy_bulk_devres *devres;
+ int ret;
+
+ *phys = NULL;
+
+ devres = devres_alloc(devm_phy_bulk_release, sizeof(*devres),
+ GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ ret = phy_bulk_get_all(dev, &devres->phys);
+ if (ret > 0) {
+ *phys = devres->phys;
+ devres->num_phys = ret;
+ devres->free_phys = true;
+ devres_add(dev, devres);
+ } else {
+ devres_free(devres);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_phy_bulk_get_all);
+
+/**
+ * devm_of_phy_bulk_get_all() - managed lookup of all PHYs from a device node
+ * @dev: device that requests the PHYs
+ * @np: device node containing the PHY references
+ * @phys: pointer to store the allocated array of struct phy_bulk_data
+ *
+ * Gets all PHYs from the specified device node, associates the allocated array
+ * and PHY references with @dev, and creates a device link for each PHY. They
+ * are automatically released on driver detach.
+ *
+ * Return: the number of PHYs on success, %0 if no PHYs are found, or a
+ * negative error code otherwise
+ */
+int devm_of_phy_bulk_get_all(struct device *dev, struct device_node *np,
+ struct phy_bulk_data **phys)
+{
+ struct phy_bulk_devres *devres;
+ int ret, i;
+
+ *phys = NULL;
+
+ devres = devres_alloc(devm_phy_bulk_release, sizeof(*devres),
+ GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ ret = of_phy_bulk_get_all(np, &devres->phys);
+ if (ret > 0) {
+ for (i = 0; i < ret; i++)
+ phy_add_device_link(dev, devres->phys[i].phy);
+ *phys = devres->phys;
+ devres->num_phys = ret;
+ devres->free_phys = true;
+ devres_add(dev, devres);
+ } else {
+ devres_free(devres);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_bulk_get_all);
+
+/**
+ * phy_bulk_init() - initialize multiple PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data to initialize
+ *
+ * Initializes the PHYs in array order. If an initialization fails, all PHYs
+ * initialized by this call are exited in reverse order.
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
+int phy_bulk_init(int num_phys, struct phy_bulk_data *phys)
+{
+ int ret, i;
+
+ if (!phys)
+ return 0;
+
+ for (i = 0; i < num_phys; i++) {
+ ret = phy_init(phys[i].phy);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (i--)
+ phy_exit(phys[i].phy);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_bulk_init);
+
+/**
+ * phy_bulk_exit() - exit multiple PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data to exit
+ *
+ * Exits the PHYs in reverse array order. All PHYs are processed even if an
+ * error occurs.
+ *
+ * Return: %0 if successful, the first negative error code otherwise
+ */
+int phy_bulk_exit(int num_phys, struct phy_bulk_data *phys)
+{
+ int ret = 0;
+ int err;
+
+ if (!phys)
+ return 0;
+
+ while (--num_phys >= 0) {
+ err = phy_exit(phys[num_phys].phy);
+ if (err && !ret)
+ ret = err;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_bulk_exit);
+
+/**
+ * phy_bulk_power_on() - power on multiple PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data to power on
+ *
+ * Powers on the PHYs in array order. If a power-on operation fails, all PHYs
+ * powered on by this call are powered off in reverse order.
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
+int phy_bulk_power_on(int num_phys, struct phy_bulk_data *phys)
+{
+ int ret, i;
+
+ if (!phys)
+ return 0;
+
+ for (i = 0; i < num_phys; i++) {
+ ret = phy_power_on(phys[i].phy);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (i--)
+ phy_power_off(phys[i].phy);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_bulk_power_on);
+
+/**
+ * phy_bulk_power_off() - power off multiple PHYs
+ * @num_phys: number of entries in the phys array
+ * @phys: array of struct phy_bulk_data to power off
+ *
+ * Powers off the PHYs in reverse array order. All PHYs are processed even if
+ * an error occurs.
+ *
+ * Return: %0 if successful, the first negative error code otherwise
+ */
+int phy_bulk_power_off(int num_phys, struct phy_bulk_data *phys)
+{
+ int ret = 0;
+ int err;
+
+ if (!phys)
+ return 0;
+
+ while (--num_phys >= 0) {
+ err = phy_power_off(phys[num_phys].phy);
+ if (err && !ret)
+ ret = err;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_bulk_power_off);
+
/**
* phy_create() - create a new phy
* @dev: device that is creating the new phy
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index ea47975e288a..97209f5d216a 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -81,6 +81,21 @@ union phy_configure_opts {
struct phy_configure_opts_hdmi hdmi;
};
+/**
+ * struct phy_bulk_data - Data used for bulk phy operations.
+ *
+ * @id: phy consumer ID
+ * @phy: struct phy * to store the associated phy
+ *
+ * The PHY APIs provide a series of phy_bulk_() API calls as
+ * a convenience to consumers which require multiple phys. This
+ * structure is used to manage data for these calls.
+ */
+struct phy_bulk_data {
+ const char *id;
+ struct phy *phy;
+};
+
/**
* struct phy_ops - set of function pointers for performing phy operations
* @init: operation to be performed for initializing phy
@@ -309,6 +324,33 @@ void devm_of_phy_provider_unregister(struct device *dev,
struct phy_provider *phy_provider);
int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id);
void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id);
+
+int phy_bulk_get(struct device *dev, int num_phys, struct phy_bulk_data *phys);
+int phy_bulk_get_optional(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys);
+int of_phy_bulk_get(struct device_node *np, int num_phys,
+ struct phy_bulk_data *phys);
+int devm_phy_bulk_get(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys);
+int devm_phy_bulk_get_optional(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys);
+int devm_of_phy_bulk_get(struct device *dev, struct device_node *np,
+ int num_phys, struct phy_bulk_data *phys);
+int phy_bulk_get_all(struct device *dev, struct phy_bulk_data **phys);
+int of_phy_bulk_get_all(struct device_node *np, struct phy_bulk_data **phys);
+int devm_phy_bulk_get_all(struct device *dev, struct phy_bulk_data **phys);
+int devm_of_phy_bulk_get_all(struct device *dev, struct device_node *np,
+ struct phy_bulk_data **phys);
+void phy_bulk_put(struct device *dev, int num_phys, struct phy_bulk_data *phys);
+void of_phy_bulk_put(int num_phys, struct phy_bulk_data *phys);
+void phy_bulk_put_all(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys);
+void of_phy_bulk_put_all(int num_phys, struct phy_bulk_data *phys);
+int phy_bulk_init(int num_phys, struct phy_bulk_data *phys);
+int phy_bulk_exit(int num_phys, struct phy_bulk_data *phys);
+int phy_bulk_power_on(int num_phys, struct phy_bulk_data *phys);
+int phy_bulk_power_off(int num_phys, struct phy_bulk_data *phys);
+
#else
static inline int phy_pm_runtime_get(struct phy *phy)
{
@@ -493,6 +535,147 @@ static inline struct phy *devm_of_phy_get_by_index(struct device *dev,
return ERR_PTR(-ENOSYS);
}
+static inline int phy_bulk_get(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int phy_bulk_get_optional(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ int i;
+
+ if (!phys)
+ return 0;
+
+ for (i = 0; i < num_phys; i++)
+ phys[i].phy = NULL;
+
+ return 0;
+}
+
+static inline int of_phy_bulk_get(struct device_node *np, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int devm_phy_bulk_get(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int devm_phy_bulk_get_optional(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ return phy_bulk_get_optional(dev, num_phys, phys);
+}
+
+static inline int devm_of_phy_bulk_get(struct device *dev,
+ struct device_node *np, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int phy_bulk_get_all(struct device *dev,
+ struct phy_bulk_data **phys)
+{
+ if (phys)
+ *phys = NULL;
+
+ return -EOPNOTSUPP;
+}
+
+static inline int of_phy_bulk_get_all(struct device_node *np,
+ struct phy_bulk_data **phys)
+{
+ if (phys)
+ *phys = NULL;
+
+ return -EOPNOTSUPP;
+}
+
+static inline int devm_phy_bulk_get_all(struct device *dev,
+ struct phy_bulk_data **phys)
+{
+ return phy_bulk_get_all(dev, phys);
+}
+
+static inline int devm_of_phy_bulk_get_all(struct device *dev,
+ struct device_node *np,
+ struct phy_bulk_data **phys)
+{
+ return of_phy_bulk_get_all(np, phys);
+}
+
+static inline void phy_bulk_put(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ if (!phys)
+ return;
+
+ while (--num_phys >= 0)
+ phys[num_phys].phy = NULL;
+}
+
+static inline void of_phy_bulk_put(int num_phys, struct phy_bulk_data *phys)
+{
+ if (!phys)
+ return;
+
+ while (--num_phys >= 0)
+ phys[num_phys].phy = NULL;
+}
+
+static inline void phy_bulk_put_all(struct device *dev, int num_phys,
+ struct phy_bulk_data *phys)
+{
+ phy_bulk_put(dev, num_phys, phys);
+}
+
+static inline void of_phy_bulk_put_all(int num_phys, struct phy_bulk_data *phys)
+{
+ of_phy_bulk_put(num_phys, phys);
+}
+
+static inline int phy_bulk_check_disabled(int num_phys,
+ struct phy_bulk_data *phys)
+{
+ int i;
+
+ if (!phys)
+ return 0;
+
+ for (i = 0; i < num_phys; i++)
+ if (phys[i].phy)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static inline int phy_bulk_init(int num_phys, struct phy_bulk_data *phys)
+{
+ return phy_bulk_check_disabled(num_phys, phys);
+}
+
+static inline int phy_bulk_exit(int num_phys, struct phy_bulk_data *phys)
+{
+ return phy_bulk_check_disabled(num_phys, phys);
+}
+
+static inline int phy_bulk_power_on(int num_phys, struct phy_bulk_data *phys)
+{
+ return phy_bulk_check_disabled(num_phys, phys);
+}
+
+static inline int phy_bulk_power_off(int num_phys, struct phy_bulk_data *phys)
+{
+ return phy_bulk_check_disabled(num_phys, phys);
+}
+
static inline void of_phy_put(struct phy *phy)
{
}
--
2.55.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] phy: core: Add common helper for get phy phandle by index
2026-08-31 2:55 ` [PATCH 2/3] phy: core: Add common helper for get phy phandle by index Inochi Amaoto
@ 2026-08-31 3:03 ` sashiko-bot
2026-08-31 9:13 ` Andy Shevchenko
1 sibling, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-08-31 3:03 UTC (permalink / raw)
To: Inochi Amaoto; +Cc: olteanv, neil.armstrong, vkoul, linux-phy
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Pre-existing race condition where the PHY module and device references are temporarily dropped before being re-acquired, risking a use-after-free.
--
commit 21ec40d6cb3e559164837267cde1582380a6bfd2
Author: Inochi Amaoto <inochiama@gmail.com>
phy: core: Add common helper for get phy phandle by index
This commit refactors phy lookup by extracting the index-based retrieval
logic from of_phy_get() and devm_of_phy_get_by_index() into a new common
helper function, of_phy_get_by_index().
> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> index 451f8f0644c13..2259f0dff0822 100644
> --- a/drivers/phy/phy-core.c
> +++ b/drivers/phy/phy-core.c
[ ... ]
> -struct phy *of_phy_get(struct device_node *np, const char *con_id)
> +static struct phy *of_phy_get_by_index(struct device_node *np, int index)
> {
> - struct phy *phy = NULL;
> - int index = 0;
> -
> - if (con_id)
> - index = of_property_match_string(np, "phy-names", con_id);
> + struct phy *phy;
>
> phy = _of_phy_get(np, index);
> if (IS_ERR(phy))
[Severity: High]
This isn't a bug introduced by this patch, but is there a race condition
here that could lead to a use-after-free?
Looking at the full body of of_phy_get_by_index():
phy = _of_phy_get(np, index);
if (IS_ERR(phy))
return phy;
if (!try_module_get(phy->ops->owner))
return ERR_PTR(-EPROBE_DEFER);
Inside _of_phy_get(), a module reference is temporarily acquired to call the
of_xlate() callback, but it is explicitly dropped via
module_put(phy_provider->owner) before returning the phy pointer.
Additionally, common of_xlate() implementations like of_phy_simple_xlate()
temporarily acquire a device reference but drop it via put_device() before
returning.
Could a concurrent sysfs unbind or module unload of the PHY driver free the
phy structure during the window between _of_phy_get() returning and
of_phy_get_by_index() evaluating phy->ops->owner?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831025506.95548-1-inochiama@gmail.com?part=2
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] phy: core: Add common helper to add phy phandle device link
2026-08-31 2:55 ` [PATCH 1/3] phy: core: Add common helper to add phy phandle device link Inochi Amaoto
2026-08-31 2:55 ` [PATCH 2/3] phy: core: Add common helper for get phy phandle by index Inochi Amaoto
2026-08-31 2:55 ` [PATCH 3/3] phy: core: Add phy bulk data helper functions Inochi Amaoto
@ 2026-08-31 3:03 ` sashiko-bot
2 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-08-31 3:03 UTC (permalink / raw)
To: Inochi Amaoto; +Cc: vkoul, neil.armstrong, linux-phy, olteanv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The `of_phy_simple_xlate` function drops the device reference before returning it, leading to a Use-After-Free if the PHY device is concurrently unregistered.
--
commit cd21d0b3786ada14d9e18e94ffc3887f9ea8b747
Author: Inochi Amaoto <inochiama@gmail.com>
phy: core: Add common helper to add phy phandle device link
It is very common for adding a device link for phy phandle
for device managed phy helper functions. So add a common
helper for future reuse.
> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> index 21aaf2f76e53e..451f8f0644c13 100644
> --- a/drivers/phy/phy-core.c
> +++ b/drivers/phy/phy-core.c
[ ... ]
> @@ -784,7 +804,6 @@ struct phy *phy_get(struct device *dev, const char *string)
> {
> int index = 0;
> struct phy *phy;
> - struct device_link *link;
[Severity: High]
This isn't a bug introduced by this patch, but could this code cause memory
corruption?
drivers/phy/phy-core.c:of_phy_simple_xlate() {
...
target_dev = class_find_device_by_of_node(&phy_class, args->np);
if (!target_dev)
return ERR_PTR(-ENODEV);
put_device(target_dev);
return to_phy(target_dev);
}
The of_phy_simple_xlate() function locates the PHY device using
class_find_device_by_of_node(), which returns it with an incremented
reference count. However, it immediately calls put_device() to drop the
reference before returning the pointer.
If a concurrent thread unregisters the PHY device (e.g., via module
removal or driver unbind), this put_device() might drop the final
reference, freeing the memory. The caller could then access the freed
pointer. Could this lead to a use-after-free race condition?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831025506.95548-1-inochiama@gmail.com?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] phy: core: Add common helper for get phy phandle by index
2026-08-31 2:55 ` [PATCH 2/3] phy: core: Add common helper for get phy phandle by index Inochi Amaoto
2026-08-31 3:03 ` sashiko-bot
@ 2026-08-31 9:13 ` Andy Shevchenko
2026-08-31 9:48 ` Inochi Amaoto
1 sibling, 1 reply; 10+ messages in thread
From: Andy Shevchenko @ 2026-08-31 9:13 UTC (permalink / raw)
To: Inochi Amaoto
Cc: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, linux-phy,
linux-kernel, Yixun Lan, Longbin Li
On Mon, Aug 31, 2026 at 10:55:04AM +0800, Inochi Amaoto wrote:
> Several phy helper use index to get phy phandle of a device node,
> add a common function for the future reuse.
...
> +/**
> + * of_phy_get_by_index() - lookup and obtain a reference to a phy using a
> + * device_node by index.
> + * @np: device_node for which to get the phy
> + * @index: index of the phy from device's point of view
> + * Returns the phy driver, after getting a refcount to it; or
> + * -ENODEV if there is no such phy. The caller is responsible for
> + * calling of_phy_put() to release that count.
Use proper syntax for Return: section, with the above kernel-doc will warn.
> + */
--
With Best Regards,
Andy Shevchenko
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 3/3] phy: core: Add phy bulk data helper functions
2026-08-31 2:55 ` [PATCH 3/3] phy: core: Add phy bulk data helper functions Inochi Amaoto
@ 2026-08-31 9:28 ` Andy Shevchenko
2026-08-31 10:00 ` Inochi Amaoto
0 siblings, 1 reply; 10+ messages in thread
From: Andy Shevchenko @ 2026-08-31 9:28 UTC (permalink / raw)
To: Inochi Amaoto
Cc: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, linux-phy,
linux-kernel, Yixun Lan, Longbin Li
On Mon, Aug 31, 2026 at 10:55:05AM +0800, Inochi Amaoto wrote:
> Add several helper functions that allow drivers to get several phy
> consumers in one operation. If any of the phy cannot be acquired then
> any phys that were got will be put before returning to the caller.
>
> This can relieve the driver owners' life who needs to handle many phys,
> as well as each phy error reporting.
...
> +/**
> + * of_phy_get_parent_count() - Get the number of phys of a device node
> + * @np: device_node for which to get the phy
> + *
> + * Return: the phy count if successful, 0 if no phy handle is found,
> + * negative error value if error occurs.
Just for the reference, here is the correct format of the kernel-doc!
> + */
> +static int of_phy_get_parent_count(const struct device_node *np)
> +{
> + int count;
> +
> + count = of_count_phandle_with_args(np, "phys", "#phy-cells");
> + if (count == -ENOENT)
> + return 0;
Why? And if so, the function perhaps needs to return unsigned type. Also
kernel-doc says about negative error codes.
> + return count;
> +}
...
> +void phy_bulk_put(struct device *dev, int num_phys, struct phy_bulk_data *phys)
> +{
> + if (!phys)
> + return;
> + while (--num_phys >= 0) {
It's hard to follow.
while (num_phys--) {
will do the job. Ditto for other similar cases.
> + if (phys[num_phys].phy)
> + phy_put(dev, phys[num_phys].phy);
> + phys[num_phys].phy = NULL;
> + }
> +}
...
> +static int __phy_bulk_get(struct device *dev, int num_phys,
> + struct phy_bulk_data *phys, bool optional)
> +{
> + int ret;
> + int i;
Do you expect num_phys to be negative?
static int __phy_bulk_get(struct device *dev, unsigned int num_phys,
...
unsigned int i;
Same comment to the rest of the similar changes.
> +
> + for (i = 0; i < num_phys; i++)
> + phys[i].phy = NULL;
> +
> + for (i = 0; i < num_phys; i++) {
> + phys[i].phy = phy_get(dev, phys[i].id);
ret = PTR_ERR_OR_ZERO(...);
> + if (IS_ERR(phys[i].phy)) {
if (ret) {
> + ret = PTR_ERR(phys[i].phy);
> + phys[i].phy = NULL;
> +
> + if (ret == -ENODEV && optional)
> + continue;
> +
> + dev_err_probe(dev, ret,
> + "Failed to get phy: (%s)\n",
There is room on the previous line.
> + phys[i].id);
> + goto err;
> + }
> + }
> +
> + return 0;
> +
> +err:
> + phy_bulk_put(dev, i, phys);
> +
> + return ret;
> +}
...
> + * Return: %0 if successful, a negative error code otherwise
Note, the reference to 0 is inconsistent with the previous changes.
Make it there [of_phy_get_parent_count()] to follow.
...
> +static int of_phy_bulk_get_by_index(struct device_node *np, int num_phys,
> + struct phy_bulk_data *phys)
> +{
> + int ret, i;
> +
> + for (i = 0; i < num_phys; i++) {
> + phys[i].id = NULL;
> + phys[i].phy = NULL;
> + }
> +
> + for (i = 0; i < num_phys; i++) {
> + of_property_read_string_index(np, "phy-names", i,
> + &phys[i].id);
The line limit is exactly 80, please fix your editor and double check that you
use as much room as available (with the correction on the logical splits where
it makes sense).
> +
> + phys[i].phy = of_phy_get_by_index(np, i);
ret = PTR_ERR_OR_ZERO(...);
?
> + if (IS_ERR(phys[i].phy)) {
> + ret = PTR_ERR(phys[i].phy);
> + phys[i].phy = NULL;
> + goto err;
> + }
> + }
> +
> + return 0;
> +
> +err:
> + of_phy_bulk_put(i, phys);
> +
> + return ret;
> +}
...
> +int of_phy_bulk_get_all(struct device_node *np, struct phy_bulk_data **phys)
> +{
> + struct phy_bulk_data *phy_bulk;
> + int num_phys;
> + int ret;
> +
> + *phys = NULL;
> + if (!np)
> + return 0;
Dup check? The OF APIs are usually NULL-aware.
Same Q to the ress of the code.
> + num_phys = of_phy_get_parent_count(np);
> + if (num_phys <= 0)
> + return num_phys;
> +
> + phy_bulk = kmalloc_objs(*phy_bulk, num_phys);
> + if (!phy_bulk)
> + return -ENOMEM;
> +
> + ret = of_phy_bulk_get_by_index(np, num_phys, phy_bulk);
> + if (ret) {
> + kfree(phy_bulk);
> + return ret;
> + }
> +
> + *phys = phy_bulk;
> +
> + return num_phys;
> +}
...
> +struct phy_bulk_devres {
> + struct phy_bulk_data *phys;
> + int num_phys;
Why signed?
> + bool free_phys;
> +};
...
I stopped here. It's too many stuff in a single patch. Please, split to two for
a starter:
- non-devm additions
- devm coverage
--
With Best Regards,
Andy Shevchenko
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] phy: core: Add common helper for get phy phandle by index
2026-08-31 9:13 ` Andy Shevchenko
@ 2026-08-31 9:48 ` Inochi Amaoto
0 siblings, 0 replies; 10+ messages in thread
From: Inochi Amaoto @ 2026-08-31 9:48 UTC (permalink / raw)
To: Andy Shevchenko, Inochi Amaoto
Cc: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, linux-phy,
linux-kernel, Yixun Lan, Longbin Li
On Mon, Aug 31, 2026 at 12:13:54PM +0300, Andy Shevchenko wrote:
> On Mon, Aug 31, 2026 at 10:55:04AM +0800, Inochi Amaoto wrote:
> > Several phy helper use index to get phy phandle of a device node,
> > add a common function for the future reuse.
>
> ...
>
> > +/**
> > + * of_phy_get_by_index() - lookup and obtain a reference to a phy using a
> > + * device_node by index.
> > + * @np: device_node for which to get the phy
> > + * @index: index of the phy from device's point of view
>
> > + * Returns the phy driver, after getting a refcount to it; or
> > + * -ENODEV if there is no such phy. The caller is responsible for
> > + * calling of_phy_put() to release that count.
>
> Use proper syntax for Return: section, with the above kernel-doc will warn.
>
Thanks, I just copy the wrony syntax. I will fix them in all the patches.
Regards,
Inochi
> > + */
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
>
> --
> linux-phy mailing list
> linux-phy@lists.infradead.org
> https://lists.infradead.org/mailman/listinfo/linux-phy
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 3/3] phy: core: Add phy bulk data helper functions
2026-08-31 9:28 ` Andy Shevchenko
@ 2026-08-31 10:00 ` Inochi Amaoto
0 siblings, 0 replies; 10+ messages in thread
From: Inochi Amaoto @ 2026-08-31 10:00 UTC (permalink / raw)
To: Andy Shevchenko, Inochi Amaoto
Cc: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, linux-phy,
linux-kernel, Yixun Lan, Longbin Li
On Mon, Aug 31, 2026 at 12:28:59PM +0300, Andy Shevchenko wrote:
> On Mon, Aug 31, 2026 at 10:55:05AM +0800, Inochi Amaoto wrote:
> > Add several helper functions that allow drivers to get several phy
> > consumers in one operation. If any of the phy cannot be acquired then
> > any phys that were got will be put before returning to the caller.
> >
> > This can relieve the driver owners' life who needs to handle many phys,
> > as well as each phy error reporting.
>
> ...
>
> > +/**
> > + * of_phy_get_parent_count() - Get the number of phys of a device node
> > + * @np: device_node for which to get the phy
> > + *
> > + * Return: the phy count if successful, 0 if no phy handle is found,
> > + * negative error value if error occurs.
>
> Just for the reference, here is the correct format of the kernel-doc!
>
> > + */
> > +static int of_phy_get_parent_count(const struct device_node *np)
> > +{
> > + int count;
> > +
> > + count = of_count_phandle_with_args(np, "phys", "#phy-cells");
>
> > + if (count == -ENOENT)
> > + return 0;
>
> Why? And if so, the function perhaps needs to return unsigned type. Also
> kernel-doc says about negative error codes.
>
There is a special reason for -ENOENT is the of_count_phandle_with_args()
return -ENOENT if is can not find "phys" property. I think it is the case
that there is no phy required for this device. So I judge it and return
0. For other errors, they should not be translated so return them as they
are.
> > + return count;
> > +}
>
> ...
>
> > +void phy_bulk_put(struct device *dev, int num_phys, struct phy_bulk_data *phys)
> > +{
> > + if (!phys)
> > + return;
>
> > + while (--num_phys >= 0) {
>
> It's hard to follow.
>
> while (num_phys--) {
>
> will do the job. Ditto for other similar cases.
>
Thanks.
> > + if (phys[num_phys].phy)
> > + phy_put(dev, phys[num_phys].phy);
> > + phys[num_phys].phy = NULL;
> > + }
> > +}
>
> ...
>
> > +static int __phy_bulk_get(struct device *dev, int num_phys,
> > + struct phy_bulk_data *phys, bool optional)
> > +{
> > + int ret;
>
> > + int i;
>
> Do you expect num_phys to be negative?
>
> static int __phy_bulk_get(struct device *dev, unsigned int num_phys,
> ...
> unsigned int i;
>
> Same comment to the rest of the similar changes.
>
No, all should be postive, this is a mistake I have made, thanks
for pointing out.
> > +
> > + for (i = 0; i < num_phys; i++)
> > + phys[i].phy = NULL;
> > +
> > + for (i = 0; i < num_phys; i++) {
> > + phys[i].phy = phy_get(dev, phys[i].id);
>
> ret = PTR_ERR_OR_ZERO(...);
>
> > + if (IS_ERR(phys[i].phy)) {
>
> if (ret) {
>
> > + ret = PTR_ERR(phys[i].phy);
> > + phys[i].phy = NULL;
> > +
> > + if (ret == -ENODEV && optional)
> > + continue;
> > +
> > + dev_err_probe(dev, ret,
> > + "Failed to get phy: (%s)\n",
>
> There is room on the previous line.
>
> > + phys[i].id);
> > + goto err;
> > + }
> > + }
> > +
> > + return 0;
> > +
> > +err:
> > + phy_bulk_put(dev, i, phys);
> > +
> > + return ret;
> > +}
>
> ...
>
> > + * Return: %0 if successful, a negative error code otherwise
>
> Note, the reference to 0 is inconsistent with the previous changes.
> Make it there [of_phy_get_parent_count()] to follow.
>
Thanks for this information.
> ...
>
> > +static int of_phy_bulk_get_by_index(struct device_node *np, int num_phys,
> > + struct phy_bulk_data *phys)
> > +{
> > + int ret, i;
> > +
> > + for (i = 0; i < num_phys; i++) {
> > + phys[i].id = NULL;
> > + phys[i].phy = NULL;
> > + }
> > +
> > + for (i = 0; i < num_phys; i++) {
> > + of_property_read_string_index(np, "phy-names", i,
> > + &phys[i].id);
>
> The line limit is exactly 80, please fix your editor and double check that you
> use as much room as available (with the correction on the logical splits where
> it makes sense).
>
Yes, you are right, I misjudge this as my completion plugin output the
arguments name. I will change that.
> > +
> > + phys[i].phy = of_phy_get_by_index(np, i);
>
> ret = PTR_ERR_OR_ZERO(...);
>
> ?
>
Right, I missed this.
> > + if (IS_ERR(phys[i].phy)) {
> > + ret = PTR_ERR(phys[i].phy);
> > + phys[i].phy = NULL;
> > + goto err;
> > + }
> > + }
> > +
> > + return 0;
> > +
> > +err:
> > + of_phy_bulk_put(i, phys);
> > +
> > + return ret;
> > +}
>
> ...
>
> > +int of_phy_bulk_get_all(struct device_node *np, struct phy_bulk_data **phys)
> > +{
> > + struct phy_bulk_data *phy_bulk;
> > + int num_phys;
> > + int ret;
> > +
> > + *phys = NULL;
>
> > + if (!np)
> > + return 0;
>
> Dup check? The OF APIs are usually NULL-aware.
> Same Q to the ress of the code.
>
Thanks, I will remove them.
> > + num_phys = of_phy_get_parent_count(np);
> > + if (num_phys <= 0)
> > + return num_phys;
> > +
> > + phy_bulk = kmalloc_objs(*phy_bulk, num_phys);
> > + if (!phy_bulk)
> > + return -ENOMEM;
> > +
> > + ret = of_phy_bulk_get_by_index(np, num_phys, phy_bulk);
> > + if (ret) {
> > + kfree(phy_bulk);
> > + return ret;
> > + }
> > +
> > + *phys = phy_bulk;
> > +
> > + return num_phys;
> > +}
>
> ...
>
> > +struct phy_bulk_devres {
> > + struct phy_bulk_data *phys;
> > + int num_phys;
>
> Why signed?
>
My mistake. It always needs to be unsigned.
> > + bool free_phys;
> > +};
>
> ...
>
> I stopped here. It's too many stuff in a single patch. Please, split to two for
> a starter:
> - non-devm additions
> - devm coverage
>
Sorry for a bad time, I will do a better check and fix them. And I will split
the patch into two in the next version.
Regards,
Inochi
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-08-31 10:00 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 2:53 [PATCH 0/3] phy: core: Add phy bulk helpers support Inochi Amaoto
2026-08-31 2:55 ` [PATCH 1/3] phy: core: Add common helper to add phy phandle device link Inochi Amaoto
2026-08-31 2:55 ` [PATCH 2/3] phy: core: Add common helper for get phy phandle by index Inochi Amaoto
2026-08-31 3:03 ` sashiko-bot
2026-08-31 9:13 ` Andy Shevchenko
2026-08-31 9:48 ` Inochi Amaoto
2026-08-31 2:55 ` [PATCH 3/3] phy: core: Add phy bulk data helper functions Inochi Amaoto
2026-08-31 9:28 ` Andy Shevchenko
2026-08-31 10:00 ` Inochi Amaoto
2026-08-31 3:03 ` [PATCH 1/3] phy: core: Add common helper to add phy phandle device link sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).