Files
Heiner Kallweit f619d5003f net: phy: factor out legacy PHY fixup support and make it always built-in
PHY fixup registration is used from platform code in init phase only.
Let's move the PHY fixup code from the modular part of phylib to the
always built-in part of phylib. This allows to annotate the fixup
registration as __init. No caller uses the return code of PHY fixup
registration, therefore change related functions to return void.

phy_needs_fixup() and phy_scan_fixups() wouldn't have to be moved to
the built-in part of phylib. But doing so allows to fully factor out
legacy fixup support into its own source code file, and make struct
phy_fixup and phy_fixup_list strictly private to phy_fixup.c.

phy_scan_fixups() is used after init phase only, then phy_fixup_list
is read-only. So we don't need the mutex when accessing the list.
Also when registering PHY fixups the mutex isn't needed, because
fixup registration is done sequentially from platform init code.
Actually there is only one platform with more than one fixup.

In addition this change lays the foundation for enabling modular
phylib on platforms where the fixup just sets a flag.

Last but not least reflect the change in the documentation,
and add a hint that fixup's are legacy functionality.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Link: https://patch.msgid.link/2d722b84-a263-400b-ac91-bf37125eb85b@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-09-18 17:26:25 -07:00

99 lines
2.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PHY fixup support
*/
#include <linux/list.h>
#include <linux/phy.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "phylib-internal.h"
static struct list_head phy_fixup_list __ro_after_init =
LIST_HEAD_INIT(phy_fixup_list);
struct phy_fixup {
struct list_head list;
char bus_id[MII_BUS_ID_SIZE + 3];
u32 phy_uid;
u32 phy_uid_mask;
int (*run)(struct phy_device *phydev);
};
/**
* phy_register_fixup - creates a new phy_fixup and adds it to the list
* @bus_id: A string which matches phydev->mdio.dev.bus_id (or NULL)
* @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
* @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
* comparison (or 0 to disable id-based matching)
* @run: The actual code to be run when a matching PHY is found
*/
static void __init phy_register_fixup(const char *bus_id, u32 phy_uid,
u32 phy_uid_mask,
int (*run)(struct phy_device *))
{
struct phy_fixup *fixup = kzalloc_obj(*fixup);
if (!fixup)
return;
if (bus_id)
strscpy(fixup->bus_id, bus_id);
fixup->phy_uid = phy_uid;
fixup->phy_uid_mask = phy_uid_mask;
fixup->run = run;
list_add_tail(&fixup->list, &phy_fixup_list);
}
/* Registers a fixup to be run on any PHY with the UID in phy_uid */
void __init phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
int (*run)(struct phy_device *))
{
phy_register_fixup(NULL, phy_uid, phy_uid_mask, run);
}
/* Registers a fixup to be run on the PHY with id string bus_id */
void __init phy_register_fixup_for_id(const char *bus_id,
int (*run)(struct phy_device *))
{
phy_register_fixup(bus_id, 0, 0, run);
}
static bool phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
{
if (!strcmp(fixup->bus_id, phydev_name(phydev)))
return true;
if (fixup->phy_uid_mask &&
phy_id_compare(phydev->phy_id, fixup->phy_uid, fixup->phy_uid_mask))
return true;
return false;
}
/**
* phy_scan_fixups - runs any matching fixups for this phydev
* @phydev: the phydev to search and run fixups for
* Returns: 0 or an errno
*/
int phy_scan_fixups(struct phy_device *phydev)
{
struct phy_fixup *fixup;
list_for_each_entry(fixup, &phy_fixup_list, list) {
if (phy_needs_fixup(phydev, fixup)) {
int err = fixup->run(phydev);
if (err < 0)
return err;
phydev->has_fixups = true;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(phy_scan_fixups);