layerscape: add patches-5.4
[openwrt/staging/chunkeey.git] / target / linux / layerscape / patches-5.4 / 701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch
1 From 0e219a6aa07754d81a25c9e4408d81d194cd2000 Mon Sep 17 00:00:00 2001
2 From: Vladimir Oltean <vladimir.oltean@nxp.com>
3 Date: Fri, 22 Nov 2019 13:45:52 +0200
4 Subject: [PATCH] net: dsa: felix: Add PCS operations for PHYLINK
5
6 This removes the bootloader dependency for SGMII PCS pre-configuration,
7 as well as adds support for monitoring the in-band SGMII AN between the
8 PCS and the system-side link partner (PHY or other MAC).
9
10 Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
11 ---
12 drivers/net/dsa/ocelot/felix.c | 23 ++-
13 drivers/net/dsa/ocelot/felix.h | 7 +-
14 drivers/net/dsa/ocelot/felix_vsc9959.c | 292 ++++++++++++++++++++++++++++++++-
15 3 files changed, 314 insertions(+), 8 deletions(-)
16
17 --- a/drivers/net/dsa/ocelot/felix.c
18 +++ b/drivers/net/dsa/ocelot/felix.c
19 @@ -265,7 +265,7 @@ static int felix_get_ts_info(struct dsa_
20 static int felix_init_structs(struct felix *felix, int num_phys_ports)
21 {
22 struct ocelot *ocelot = &felix->ocelot;
23 - resource_size_t base;
24 + resource_size_t switch_base;
25 int port, i, err;
26
27 ocelot->num_phys_ports = num_phys_ports;
28 @@ -281,7 +281,8 @@ static int felix_init_structs(struct fel
29 ocelot->ops = felix->info->ops;
30 ocelot->quirks = felix->info->quirks;
31
32 - base = pci_resource_start(felix->pdev, felix->info->pci_bar);
33 + switch_base = pci_resource_start(felix->pdev,
34 + felix->info->switch_pci_bar);
35
36 for (i = 0; i < TARGET_MAX; i++) {
37 struct regmap *target;
38 @@ -292,8 +293,8 @@ static int felix_init_structs(struct fel
39
40 res = &felix->info->target_io_res[i];
41 res->flags = IORESOURCE_MEM;
42 - res->start += base;
43 - res->end += base;
44 + res->start += switch_base;
45 + res->end += switch_base;
46
47 target = ocelot_regmap_init(ocelot, res);
48 if (IS_ERR(target)) {
49 @@ -327,8 +328,8 @@ static int felix_init_structs(struct fel
50
51 res = &felix->info->port_io_res[port];
52 res->flags = IORESOURCE_MEM;
53 - res->start += base;
54 - res->end += base;
55 + res->start += switch_base;
56 + res->end += switch_base;
57
58 port_regs = devm_ioremap_resource(ocelot->dev, res);
59 if (IS_ERR(port_regs)) {
60 @@ -342,6 +343,12 @@ static int felix_init_structs(struct fel
61 ocelot->ports[port] = ocelot_port;
62 }
63
64 + if (felix->info->mdio_bus_alloc) {
65 + err = felix->info->mdio_bus_alloc(ocelot);
66 + if (err < 0)
67 + return err;
68 + }
69 +
70 return 0;
71 }
72
73 @@ -377,6 +384,10 @@ static int felix_setup(struct dsa_switch
74 static void felix_teardown(struct dsa_switch *ds)
75 {
76 struct ocelot *ocelot = ds->priv;
77 + struct felix *felix = ocelot_to_felix(ocelot);
78 +
79 + if (felix->imdio)
80 + mdiobus_unregister(felix->imdio);
81
82 /* stop workqueue thread */
83 ocelot_deinit(ocelot);
84 --- a/drivers/net/dsa/ocelot/felix.h
85 +++ b/drivers/net/dsa/ocelot/felix.h
86 @@ -10,6 +10,7 @@
87 struct felix_info {
88 struct resource *target_io_res;
89 struct resource *port_io_res;
90 + struct resource *imdio_res;
91 const struct reg_field *regfields;
92 const u32 *const *map;
93 const struct ocelot_ops *ops;
94 @@ -17,8 +18,10 @@ struct felix_info {
95 const struct ocelot_stat_layout *stats_layout;
96 unsigned int num_stats;
97 int num_ports;
98 - int pci_bar;
99 + int switch_pci_bar;
100 + int imdio_pci_bar;
101 unsigned long quirks;
102 + int (*mdio_bus_alloc)(struct ocelot *ocelot);
103 };
104
105 extern struct felix_info felix_info_vsc9959;
106 @@ -33,6 +36,8 @@ struct felix {
107 struct pci_dev *pdev;
108 struct felix_info *info;
109 struct ocelot ocelot;
110 + struct mii_bus *imdio;
111 + struct phy_device **pcs;
112 };
113
114 #endif
115 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c
116 +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
117 @@ -2,6 +2,7 @@
118 /* Copyright 2017 Microsemi Corporation
119 * Copyright 2018-2019 NXP Semiconductors
120 */
121 +#include <linux/fsl/enetc_mdio.h>
122 #include <soc/mscc/ocelot_sys.h>
123 #include <soc/mscc/ocelot.h>
124 #include <linux/iopoll.h>
125 @@ -390,6 +391,15 @@ static struct resource vsc9959_port_io_r
126 },
127 };
128
129 +/* Port MAC 0 Internal MDIO bus through which the SerDes acting as an
130 + * SGMII/QSGMII MAC PCS can be found.
131 + */
132 +static struct resource vsc9959_imdio_res = {
133 + .start = 0x8030,
134 + .end = 0x8040,
135 + .name = "imdio",
136 +};
137 +
138 static const struct reg_field vsc9959_regfields[] = {
139 [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 6, 6),
140 [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 5),
141 @@ -569,13 +579,291 @@ static int vsc9959_reset(struct ocelot *
142 return 0;
143 }
144
145 +void vsc9959_pcs_validate(struct ocelot *ocelot, int port,
146 + unsigned long *supported,
147 + struct phylink_link_state *state)
148 +{
149 + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
150 +
151 + if (state->interface != PHY_INTERFACE_MODE_NA &&
152 + state->interface != PHY_INTERFACE_MODE_GMII &&
153 + state->interface != PHY_INTERFACE_MODE_SGMII &&
154 + state->interface != PHY_INTERFACE_MODE_QSGMII &&
155 + state->interface != PHY_INTERFACE_MODE_USXGMII) {
156 + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
157 + return;
158 + }
159 +
160 + /* No half-duplex. */
161 + phylink_set_port_modes(mask);
162 + phylink_set(mask, Autoneg);
163 + phylink_set(mask, Pause);
164 + phylink_set(mask, Asym_Pause);
165 + phylink_set(mask, 10baseT_Full);
166 + phylink_set(mask, 100baseT_Full);
167 + phylink_set(mask, 1000baseT_Full);
168 + phylink_set(mask, 1000baseX_Full);
169 + phylink_set(mask, 2500baseT_Full);
170 + phylink_set(mask, 2500baseX_Full);
171 + phylink_set(mask, 1000baseKX_Full);
172 +
173 + bitmap_and(supported, supported, mask,
174 + __ETHTOOL_LINK_MODE_MASK_NBITS);
175 + bitmap_and(state->advertising, state->advertising, mask,
176 + __ETHTOOL_LINK_MODE_MASK_NBITS);
177 +}
178 +
179 +static void vsc9959_pcs_an_restart(struct ocelot *ocelot, int port)
180 +{
181 + struct felix *felix = ocelot_to_felix(ocelot);
182 + struct phy_device *pcs = felix->pcs[port];
183 +
184 + if (!pcs)
185 + return;
186 +
187 + phy_set_bits(pcs, MII_BMCR, BMCR_ANRESTART);
188 +}
189 +
190 +static void vsc9959_pcs_init_sgmii(struct phy_device *pcs,
191 + unsigned int link_an_mode,
192 + const struct phylink_link_state *state)
193 +{
194 + /* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001
195 + * for the MAC PCS in order to acknowledge the AN.
196 + */
197 + phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | ADVERTISE_LPACK);
198 +
199 + /* Set to SGMII mode, use AN */
200 + phy_write(pcs, ENETC_PCS_IF_MODE, ENETC_PCS_IF_MODE_SGMII_AN);
201 +
202 + /* Adjust link timer for SGMII */
203 + phy_write(pcs, ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL);
204 + phy_write(pcs, ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL);
205 +
206 + phy_write(pcs, MII_BMCR, BMCR_SPEED1000 |
207 + BMCR_FULLDPLX |
208 + BMCR_ANENABLE);
209 +}
210 +
211 +#define ADVERTISE_USXGMII_FDX BIT(12)
212 +
213 +static void vsc9959_pcs_init_sxgmii(struct phy_device *pcs,
214 + unsigned int link_an_mode,
215 + const struct phylink_link_state *state)
216 +{
217 + /* Configure device ability for the USXGMII Replicator */
218 + phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE,
219 + ADVERTISE_SGMII |
220 + ADVERTISE_LPACK |
221 + ADVERTISE_USXGMII_FDX);
222 +}
223 +
224 +static void vsc9959_pcs_init(struct ocelot *ocelot, int port,
225 + unsigned int link_an_mode,
226 + const struct phylink_link_state *state)
227 +{
228 + struct felix *felix = ocelot_to_felix(ocelot);
229 + struct phy_device *pcs = felix->pcs[port];
230 +
231 + if (!pcs)
232 + return;
233 +
234 + if (link_an_mode == MLO_AN_FIXED) {
235 + phydev_err(pcs, "Fixed modes are not yet supported.\n");
236 + return;
237 + }
238 +
239 + pcs->interface = state->interface;
240 + if (pcs->interface == PHY_INTERFACE_MODE_USXGMII)
241 + pcs->is_c45 = true;
242 + else
243 + pcs->is_c45 = false;
244 +
245 + /* The PCS does not implement the BMSR register fully, so capability
246 + * detection via genphy_read_abilities does not work. Since we can get
247 + * the PHY config word from the LPA register though, there is still
248 + * value in using the generic phy_resolve_aneg_linkmode function. So
249 + * populate the supported and advertising link modes manually here.
250 + */
251 + linkmode_set_bit_array(phy_basic_ports_array,
252 + ARRAY_SIZE(phy_basic_ports_array),
253 + pcs->supported);
254 + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, pcs->supported);
255 + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported);
256 + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported);
257 + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported);
258 + phy_advertise_supported(pcs);
259 +
260 + switch (pcs->interface) {
261 + case PHY_INTERFACE_MODE_SGMII:
262 + case PHY_INTERFACE_MODE_QSGMII:
263 + vsc9959_pcs_init_sgmii(pcs, link_an_mode, state);
264 + break;
265 + case PHY_INTERFACE_MODE_USXGMII:
266 + vsc9959_pcs_init_sxgmii(pcs, link_an_mode, state);
267 + break;
268 + default:
269 + dev_err(ocelot->dev, "Unsupported link mode %s\n",
270 + phy_modes(pcs->interface));
271 + }
272 +}
273 +
274 +static void vsc9959_pcs_link_state(struct ocelot *ocelot, int port,
275 + struct phylink_link_state *state)
276 +{
277 + struct felix *felix = ocelot_to_felix(ocelot);
278 + struct phy_device *pcs = felix->pcs[port];
279 + int err;
280 +
281 + if (!pcs)
282 + return;
283 +
284 + /* Reading PCS status not yet supported for USXGMII */
285 + if (pcs->is_c45) {
286 + state->link = 1;
287 + return;
288 + }
289 +
290 + pcs->speed = SPEED_UNKNOWN;
291 + pcs->duplex = DUPLEX_UNKNOWN;
292 + pcs->pause = 0;
293 + pcs->asym_pause = 0;
294 +
295 + err = genphy_update_link(pcs);
296 + if (err < 0)
297 + return;
298 +
299 + if (pcs->autoneg_complete) {
300 + u16 lpa = phy_read(pcs, MII_LPA);
301 +
302 + switch (state->interface) {
303 + case PHY_INTERFACE_MODE_SGMII:
304 + case PHY_INTERFACE_MODE_QSGMII:
305 + mii_lpa_to_linkmode_lpa_sgmii(pcs->lp_advertising, lpa);
306 + break;
307 + default:
308 + return;
309 + }
310 +
311 + phy_resolve_aneg_linkmode(pcs);
312 + }
313 +
314 + state->an_complete = pcs->autoneg_complete;
315 + state->an_enabled = pcs->autoneg;
316 + state->link = pcs->link;
317 + state->duplex = pcs->duplex;
318 + state->speed = pcs->speed;
319 + /* SGMII AN does not negotiate flow control, but that's ok,
320 + * since phylink already knows that, and does:
321 + * link_state.pause |= pl->phy_state.pause;
322 + */
323 + state->pause = pcs->pause;
324 +
325 + dev_dbg(ocelot->dev,
326 + "%s: mode=%s/%s/%s adv=%*pb lpa=%*pb link=%u an_enabled=%u an_complete=%u\n",
327 + __func__,
328 + phy_modes(state->interface),
329 + phy_speed_to_str(state->speed),
330 + phy_duplex_to_str(state->duplex),
331 + __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
332 + __ETHTOOL_LINK_MODE_MASK_NBITS, state->lp_advertising,
333 + state->link, state->an_enabled, state->an_complete);
334 +}
335 +
336 static const struct ocelot_ops vsc9959_ops = {
337 .reset = vsc9959_reset,
338 + .pcs_init = vsc9959_pcs_init,
339 + .pcs_an_restart = vsc9959_pcs_an_restart,
340 + .pcs_link_state = vsc9959_pcs_link_state,
341 + .pcs_validate = vsc9959_pcs_validate,
342 };
343
344 +static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
345 +{
346 + struct felix *felix = ocelot_to_felix(ocelot);
347 + struct enetc_mdio_priv *mdio_priv;
348 + struct device *dev = ocelot->dev;
349 + resource_size_t imdio_base;
350 + void __iomem *imdio_regs;
351 + struct resource *res;
352 + struct enetc_hw *hw;
353 + struct mii_bus *bus;
354 + int port;
355 + int rc;
356 +
357 + felix->pcs = devm_kcalloc(dev, felix->info->num_ports,
358 + sizeof(struct phy_device),
359 + GFP_KERNEL);
360 + if (!felix->pcs) {
361 + dev_err(dev, "failed to allocate array for PCS PHYs\n");
362 + return -ENOMEM;
363 + }
364 +
365 + imdio_base = pci_resource_start(felix->pdev,
366 + felix->info->imdio_pci_bar);
367 +
368 + res = felix->info->imdio_res;
369 + res->flags = IORESOURCE_MEM;
370 + res->start += imdio_base;
371 + res->end += imdio_base;
372 +
373 + imdio_regs = devm_ioremap_resource(dev, res);
374 + if (IS_ERR(imdio_regs)) {
375 + dev_err(dev, "failed to map internal MDIO registers\n");
376 + return PTR_ERR(imdio_regs);
377 + }
378 +
379 + hw = enetc_hw_alloc(dev, imdio_regs);
380 + if (IS_ERR(hw)) {
381 + dev_err(dev, "failed to allocate ENETC HW structure\n");
382 + return PTR_ERR(hw);
383 + }
384 +
385 + bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
386 + if (!bus)
387 + return -ENOMEM;
388 +
389 + bus->name = "VSC9959 internal MDIO bus";
390 + bus->read = enetc_mdio_read;
391 + bus->write = enetc_mdio_write;
392 + bus->parent = dev;
393 + mdio_priv = bus->priv;
394 + mdio_priv->hw = hw;
395 + /* This gets added to imdio_regs, which already maps addresses
396 + * starting with the proper offset.
397 + */
398 + mdio_priv->mdio_base = 0;
399 + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
400 +
401 + /* Needed in order to initialize the bus mutex lock */
402 + rc = mdiobus_register(bus);
403 + if (rc < 0) {
404 + dev_err(dev, "failed to register MDIO bus\n");
405 + return rc;
406 + }
407 +
408 + felix->imdio = bus;
409 +
410 + for (port = 0; port < felix->info->num_ports; port++) {
411 + struct phy_device *pcs;
412 + bool is_c45 = false;
413 +
414 + pcs = get_phy_device(felix->imdio, port, is_c45);
415 + if (IS_ERR(pcs))
416 + continue;
417 +
418 + felix->pcs[port] = pcs;
419 +
420 + dev_info(dev, "Found PCS at internal MDIO address %d\n", port);
421 + }
422 +
423 + return 0;
424 +}
425 +
426 struct felix_info felix_info_vsc9959 = {
427 .target_io_res = vsc9959_target_io_res,
428 .port_io_res = vsc9959_port_io_res,
429 + .imdio_res = &vsc9959_imdio_res,
430 .regfields = vsc9959_regfields,
431 .map = vsc9959_regmap,
432 .ops = &vsc9959_ops,
433 @@ -583,6 +871,8 @@ struct felix_info felix_info_vsc9959 = {
434 .num_stats = ARRAY_SIZE(vsc9959_stats_layout),
435 .shared_queue_sz = 128 * 1024,
436 .num_ports = 6,
437 - .pci_bar = 4,
438 + .switch_pci_bar = 4,
439 + .imdio_pci_bar = 0,
440 .quirks = OCELOT_PCS_PERFORMS_RATE_ADAPTATION,
441 + .mdio_bus_alloc = vsc9959_mdio_bus_alloc,
442 };