1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
// 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);
|