d826877e7dc69315fc69af97642438f2f76e38a8
[openwrt/staging/ldir.git] /
1 From d1e86325af377129adb7fc6f34eb044ca6068b47 Mon Sep 17 00:00:00 2001
2 From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
3 Date: Wed, 15 Dec 2021 15:34:15 +0000
4 Subject: [PATCH] net: phylink: add mac_select_pcs() method to phylink_mac_ops
5
6 mac_select_pcs() allows us to have an explicit point to query which
7 PCS the MAC wishes to use for a particular PHY interface mode, thereby
8 allowing us to add support to validate the link settings with the PCS.
9
10 Phylink will also use this to select the PCS to be used during a major
11 configuration event without the MAC driver needing to call
12 phylink_set_pcs().
13
14 Note that if mac_select_pcs() is present, the supported_interfaces
15 bitmap must be filled in; this avoids mac_select_pcs() being called
16 with PHY_INTERFACE_MODE_NA when we want to get support for all
17 interface types. Phylink will return an error in phylink_create()
18 unless this condition is satisfied.
19
20 Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
21 Signed-off-by: David S. Miller <davem@davemloft.net>
22 ---
23 drivers/net/phy/phylink.c | 68 +++++++++++++++++++++++++++++++++------
24 include/linux/phylink.h | 18 +++++++++++
25 2 files changed, 77 insertions(+), 9 deletions(-)
26
27 --- a/drivers/net/phy/phylink.c
28 +++ b/drivers/net/phy/phylink.c
29 @@ -155,6 +155,23 @@ static const char *phylink_an_mode_str(u
30 return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
31 }
32
33 +static int phylink_validate_mac_and_pcs(struct phylink *pl,
34 + unsigned long *supported,
35 + struct phylink_link_state *state)
36 +{
37 + struct phylink_pcs *pcs;
38 +
39 + if (pl->mac_ops->mac_select_pcs) {
40 + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
41 + if (IS_ERR(pcs))
42 + return PTR_ERR(pcs);
43 + }
44 +
45 + pl->mac_ops->validate(pl->config, supported, state);
46 +
47 + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
48 +}
49 +
50 static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
51 struct phylink_link_state *state)
52 {
53 @@ -170,9 +187,10 @@ static int phylink_validate_any(struct p
54
55 t = *state;
56 t.interface = intf;
57 - pl->mac_ops->validate(pl->config, s, &t);
58 - linkmode_or(all_s, all_s, s);
59 - linkmode_or(all_adv, all_adv, t.advertising);
60 + if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
61 + linkmode_or(all_s, all_s, s);
62 + linkmode_or(all_adv, all_adv, t.advertising);
63 + }
64 }
65 }
66
67 @@ -194,9 +212,7 @@ static int phylink_validate(struct phyli
68 return -EINVAL;
69 }
70
71 - pl->mac_ops->validate(pl->config, supported, state);
72 -
73 - return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
74 + return phylink_validate_mac_and_pcs(pl, supported, state);
75 }
76
77 static int phylink_parse_fixedlink(struct phylink *pl,
78 @@ -486,10 +502,21 @@ static void phylink_mac_pcs_an_restart(s
79 static void phylink_major_config(struct phylink *pl, bool restart,
80 const struct phylink_link_state *state)
81 {
82 + struct phylink_pcs *pcs = NULL;
83 int err;
84
85 phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
86
87 + if (pl->mac_ops->mac_select_pcs) {
88 + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
89 + if (IS_ERR(pcs)) {
90 + phylink_err(pl,
91 + "mac_select_pcs unexpectedly failed: %pe\n",
92 + pcs);
93 + return;
94 + }
95 + }
96 +
97 if (pl->mac_ops->mac_prepare) {
98 err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
99 state->interface);
100 @@ -500,6 +527,12 @@ static void phylink_major_config(struct
101 }
102 }
103
104 + /* If we have a new PCS, switch to the new PCS after preparing the MAC
105 + * for the change.
106 + */
107 + if (pcs)
108 + phylink_set_pcs(pl, pcs);
109 +
110 phylink_mac_config(pl, state);
111
112 if (pl->pcs_ops) {
113 @@ -879,6 +912,14 @@ struct phylink *phylink_create(struct ph
114 struct phylink *pl;
115 int ret;
116
117 + /* Validate the supplied configuration */
118 + if (mac_ops->mac_select_pcs &&
119 + phy_interface_empty(config->supported_interfaces)) {
120 + dev_err(config->dev,
121 + "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
122 + return ERR_PTR(-EINVAL);
123 + }
124 +
125 pl = kzalloc(sizeof(*pl), GFP_KERNEL);
126 if (!pl)
127 return ERR_PTR(-ENOMEM);
128 @@ -946,9 +987,10 @@ EXPORT_SYMBOL_GPL(phylink_create);
129 * @pl: a pointer to a &struct phylink returned from phylink_create()
130 * @pcs: a pointer to the &struct phylink_pcs
131 *
132 - * Bind the MAC PCS to phylink. This may be called after phylink_create(),
133 - * in mac_prepare() or mac_config() methods if it is desired to dynamically
134 - * change the PCS.
135 + * Bind the MAC PCS to phylink. This may be called after phylink_create().
136 + * If it is desired to dynamically change the PCS, then the preferred method
137 + * is to use mac_select_pcs(), but it may also be called in mac_prepare()
138 + * or mac_config().
139 *
140 * Please note that there are behavioural changes with the mac_config()
141 * callback if a PCS is present (denoting a newer setup) so removing a PCS
142 @@ -959,6 +1001,14 @@ void phylink_set_pcs(struct phylink *pl,
143 {
144 pl->pcs = pcs;
145 pl->pcs_ops = pcs->ops;
146 +
147 + if (!pl->phylink_disable_state &&
148 + pl->cfg_link_an_mode == MLO_AN_INBAND) {
149 + if (pl->config->pcs_poll || pcs->poll)
150 + mod_timer(&pl->link_poll, jiffies + HZ);
151 + else
152 + del_timer(&pl->link_poll);
153 + }
154 }
155 EXPORT_SYMBOL_GPL(phylink_set_pcs);
156
157 --- a/include/linux/phylink.h
158 +++ b/include/linux/phylink.h
159 @@ -86,6 +86,7 @@ struct phylink_config {
160 /**
161 * struct phylink_mac_ops - MAC operations structure.
162 * @validate: Validate and update the link configuration.
163 + * @mac_select_pcs: Select a PCS for the interface mode.
164 * @mac_pcs_get_state: Read the current link state from the hardware.
165 * @mac_prepare: prepare for a major reconfiguration of the interface.
166 * @mac_config: configure the MAC for the selected mode and state.
167 @@ -100,6 +101,8 @@ struct phylink_mac_ops {
168 void (*validate)(struct phylink_config *config,
169 unsigned long *supported,
170 struct phylink_link_state *state);
171 + struct phylink_pcs *(*mac_select_pcs)(struct phylink_config *config,
172 + phy_interface_t interface);
173 void (*mac_pcs_get_state)(struct phylink_config *config,
174 struct phylink_link_state *state);
175 int (*mac_prepare)(struct phylink_config *config, unsigned int mode,
176 @@ -152,6 +155,21 @@ struct phylink_mac_ops {
177 */
178 void validate(struct phylink_config *config, unsigned long *supported,
179 struct phylink_link_state *state);
180 +/**
181 + * mac_select_pcs: Select a PCS for the interface mode.
182 + * @config: a pointer to a &struct phylink_config.
183 + * @interface: PHY interface mode for PCS
184 + *
185 + * Return the &struct phylink_pcs for the specified interface mode, or
186 + * NULL if none is required, or an error pointer on error.
187 + *
188 + * This must not modify any state. It is used to query which PCS should
189 + * be used. Phylink will use this during validation to ensure that the
190 + * configuration is valid, and when setting a configuration to internally
191 + * set the PCS that will be used.
192 + */
193 +struct phylink_pcs *mac_select_pcs(struct phylink_config *config,
194 + phy_interface_t interface);
195
196 /**
197 * mac_pcs_get_state() - Read the current inband link state from the hardware