Skip to content

Commit bdeced7

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: felix: Add PCS operations for PHYLINK
Layerscape SoCs traditionally expose the SerDes configuration/status for Ethernet protocols (PCS for SGMII/USXGMII/10GBase-R etc etc) in a register format that is compatible with clause 22 or clause 45 (depending on SerDes protocol). Each MAC has its own internal MDIO bus on which there is one or more of these PCS's, responding to commands at a configurable PHY address. The per-port internal MDIO bus (which is just for PCSs) is totally separate and has nothing to do with the dedicated external MDIO controller (which is just for PHYs), but the register map for the MDIO controller is the same. The VSC9959 (Felix) switch instantiated in the LS1028A is integrated in hardware with the ENETC PCS of its DSA master, and reuses its MDIO controller driver, so Felix has been made to depend on it in Kconfig. +------------------------------------------------------------------------+ | +--------+ GMII (typically disabled via RCW) | | ENETC PCI | ENETC |--------------------------+ | | Root Complex | port 3 |-----------------------+ | | | Integrated +--------+ | | | | Endpoint | | | | +--------+ 2.5G GMII | | | | | ENETC |--------------+ | | | | | port 2 |-----------+ | | | | | +--------+ | | | | | | +--------+ +--------+ | | | Felix | | Felix | | | | port 4 | | port 5 | | | +--------+ +--------+ | | | | +--------+ +--------+ +--------+ +--------+ +--------+ +--------+ | | | ENETC | | ENETC | | Felix | | Felix | | Felix | | Felix | | | | port 0 | | port 1 | | port 0 | | port 1 | | port 2 | | port 3 | | +------------------------------------------------------------------------+ | |||| SerDes | |||| |||| |||| |||| | | +--------+block | +--------------------------------------------+ | | | ENETC | | | ENETC port 2 internal MDIO bus | | | | port 0 | | | PCS PCS PCS PCS | | | | PCS | | | 0 1 2 3 | | +-----------------|------------------------------------------------------+ v v v v v v SGMII/ RGMII QSGMII/QSXGMII/4xSGMII/4x1000Base-X/4x2500Base-X USXGMII/ (bypasses 1000Base-X/ SerDes) 2500Base-X In the LS1028A SoC described above, the VSC9959 Felix switch is PF5 of the ENETC root complex, and has 2 BARs: - BAR 4: the switch's effective registers - BAR 0: the MDIO controller register map lended from ENETC port 2 (PF2), for accessing its associated PCS's. This explanation is necessary because the patch does some renaming "pci_bar" -> "switch_pci_bar" for clarity, which would otherwise appear a bit obtuse. The fact that the internal MDIO bus is "borrowed" is relevant because the register map is found in PF5 (the switch) but it triggers an access fault if PF2 (the ENETC DSA master) is not enabled. This is not treated in any way (and I don't think it can be treated). All of this is so SoC-specific, that it was contained as much as possible in the platform-integration file felix_vsc9959.c. We need to parse and pre-validate the device tree because of 2 reasons: - The PHY mode (SerDes protocol) cannot change at runtime due to SoC design. - There is a circular dependency in that we need to know what clause the PCS speaks in order to find it on the internal MDIO bus. But the clause of the PCS depends on what phy-mode it is configured for. The goal of this patch is to make steps towards removing the bootloader dependency for SGMII PCS pre-configuration, as well as to add support for monitoring the in-band SGMII AN between the PCS and the system-side link partner (PHY or other MAC). In practice the bootloader dependency is not completely removed. U-Boot pre-programs the PHY address at which each PCS can be found on the internal MDIO bus (MDEV_PORT). This is needed because the PCS of each port has the same out-of-reset PHY address of zero. The SerDes register for changing MDEV_PORT is pretty deep in the SoC (outside the addresses of the ENETC PCI BARs) and therefore inaccessible to us from here. Felix VSC9959 and Ocelot VSC7514 are integrated very differently in their respective SoCs, and for that reason Felix does not use the Ocelot core library for PHYLINK. On one hand we don't want to impose the fixed phy-mode limitation to Ocelot, and on the other hand Felix doesn't need to force the MAC link speed the way Ocelot does, since the MAC is connected to the PCS through a fixed GMII, and the PCS is the one who does the rate adaptation at lower link speeds, which the MAC does not even need to know about. In fact changing the GMII speed for Felix irrecoverably breaks transmission through that port until a reset. The pair with ENETC port 3 and Felix port 5 is optional and doesn't support tagging. When we enable it, swp5 is a regular slave port, albeit an internal one. The trouble is that it doesn't work, and that is because the DSA PHYLIB adaptation layer doesn't treat fixed-link slave ports. So that is yet another reason for wanting to convert Felix to the native PHYLINK API. Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 964ee5c commit bdeced7

File tree

4 files changed

+767
-17
lines changed

4 files changed

+767
-17
lines changed

drivers/net/dsa/ocelot/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ config NET_DSA_MSCC_FELIX
33
tristate "Ocelot / Felix Ethernet switch support"
44
depends on NET_DSA && PCI
55
depends on NET_VENDOR_MICROSEMI
6+
depends on NET_VENDOR_FREESCALE
67
select MSCC_OCELOT_SWITCH
78
select NET_DSA_TAG_OCELOT
9+
select FSL_ENETC_MDIO
810
help
911
This driver supports the VSC9959 network switch, which is a member of
1012
the Vitesse / Microsemi / Microchip Ocelot family of switching cores.

drivers/net/dsa/ocelot/felix.c

Lines changed: 250 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22
/* Copyright 2019 NXP Semiconductors
33
*/
44
#include <uapi/linux/if_bridge.h>
5+
#include <soc/mscc/ocelot_qsys.h>
6+
#include <soc/mscc/ocelot_sys.h>
7+
#include <soc/mscc/ocelot_dev.h>
8+
#include <soc/mscc/ocelot_ana.h>
59
#include <soc/mscc/ocelot.h>
610
#include <linux/packing.h>
711
#include <linux/module.h>
12+
#include <linux/of_net.h>
813
#include <linux/pci.h>
914
#include <linux/of.h>
1015
#include <net/dsa.h>
@@ -26,14 +31,6 @@ static int felix_set_ageing_time(struct dsa_switch *ds,
2631
return 0;
2732
}
2833

29-
static void felix_adjust_link(struct dsa_switch *ds, int port,
30-
struct phy_device *phydev)
31-
{
32-
struct ocelot *ocelot = ds->priv;
33-
34-
ocelot_adjust_link(ocelot, port, phydev);
35-
}
36-
3734
static int felix_fdb_dump(struct dsa_switch *ds, int port,
3835
dsa_fdb_dump_cb_t *cb, void *data)
3936
{
@@ -155,6 +152,138 @@ static void felix_port_disable(struct dsa_switch *ds, int port)
155152
return ocelot_port_disable(ocelot, port);
156153
}
157154

155+
static void felix_phylink_validate(struct dsa_switch *ds, int port,
156+
unsigned long *supported,
157+
struct phylink_link_state *state)
158+
{
159+
struct ocelot *ocelot = ds->priv;
160+
struct ocelot_port *ocelot_port = ocelot->ports[port];
161+
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
162+
163+
if (state->interface != PHY_INTERFACE_MODE_NA &&
164+
state->interface != ocelot_port->phy_mode) {
165+
bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
166+
return;
167+
}
168+
169+
/* No half-duplex. */
170+
phylink_set_port_modes(mask);
171+
phylink_set(mask, Autoneg);
172+
phylink_set(mask, Pause);
173+
phylink_set(mask, Asym_Pause);
174+
if (state->interface != PHY_INTERFACE_MODE_2500BASEX) {
175+
phylink_set(mask, 10baseT_Full);
176+
phylink_set(mask, 100baseT_Full);
177+
phylink_set(mask, 1000baseT_Full);
178+
}
179+
/* The internal ports that run at 2.5G are overclocked GMII */
180+
if (state->interface == PHY_INTERFACE_MODE_GMII ||
181+
state->interface == PHY_INTERFACE_MODE_2500BASEX ||
182+
state->interface == PHY_INTERFACE_MODE_USXGMII) {
183+
phylink_set(mask, 2500baseT_Full);
184+
phylink_set(mask, 2500baseX_Full);
185+
}
186+
187+
bitmap_and(supported, supported, mask,
188+
__ETHTOOL_LINK_MODE_MASK_NBITS);
189+
bitmap_and(state->advertising, state->advertising, mask,
190+
__ETHTOOL_LINK_MODE_MASK_NBITS);
191+
}
192+
193+
static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port,
194+
struct phylink_link_state *state)
195+
{
196+
struct ocelot *ocelot = ds->priv;
197+
struct felix *felix = ocelot_to_felix(ocelot);
198+
199+
if (felix->info->pcs_link_state)
200+
felix->info->pcs_link_state(ocelot, port, state);
201+
202+
return 0;
203+
}
204+
205+
static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
206+
unsigned int link_an_mode,
207+
const struct phylink_link_state *state)
208+
{
209+
struct ocelot *ocelot = ds->priv;
210+
struct ocelot_port *ocelot_port = ocelot->ports[port];
211+
struct felix *felix = ocelot_to_felix(ocelot);
212+
u32 mac_fc_cfg;
213+
214+
/* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and
215+
* PORT_RST bits in CLOCK_CFG
216+
*/
217+
ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(state->speed),
218+
DEV_CLOCK_CFG);
219+
220+
/* Flow control. Link speed is only used here to evaluate the time
221+
* specification in incoming pause frames.
222+
*/
223+
mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(state->speed);
224+
if (state->pause & MLO_PAUSE_RX)
225+
mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA;
226+
if (state->pause & MLO_PAUSE_TX)
227+
mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA |
228+
SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
229+
SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
230+
SYS_MAC_FC_CFG_ZERO_PAUSE_ENA;
231+
ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port);
232+
233+
ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
234+
235+
if (felix->info->pcs_init)
236+
felix->info->pcs_init(ocelot, port, link_an_mode, state);
237+
}
238+
239+
static void felix_phylink_mac_an_restart(struct dsa_switch *ds, int port)
240+
{
241+
struct ocelot *ocelot = ds->priv;
242+
struct felix *felix = ocelot_to_felix(ocelot);
243+
244+
if (felix->info->pcs_an_restart)
245+
felix->info->pcs_an_restart(ocelot, port);
246+
}
247+
248+
static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
249+
unsigned int link_an_mode,
250+
phy_interface_t interface)
251+
{
252+
struct ocelot *ocelot = ds->priv;
253+
struct ocelot_port *ocelot_port = ocelot->ports[port];
254+
255+
ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG);
256+
ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA,
257+
QSYS_SWITCH_PORT_MODE, port);
258+
}
259+
260+
static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
261+
unsigned int link_an_mode,
262+
phy_interface_t interface,
263+
struct phy_device *phydev)
264+
{
265+
struct ocelot *ocelot = ds->priv;
266+
struct ocelot_port *ocelot_port = ocelot->ports[port];
267+
268+
/* Enable MAC module */
269+
ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA |
270+
DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
271+
272+
/* Enable receiving frames on the port, and activate auto-learning of
273+
* MAC addresses.
274+
*/
275+
ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO |
276+
ANA_PORT_PORT_CFG_RECV_ENA |
277+
ANA_PORT_PORT_CFG_PORTID_VAL(port),
278+
ANA_PORT_PORT_CFG, port);
279+
280+
/* Core: Enable port for frame transfer */
281+
ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE |
282+
QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) |
283+
QSYS_SWITCH_PORT_MODE_PORT_ENA,
284+
QSYS_SWITCH_PORT_MODE, port);
285+
}
286+
158287
static void felix_get_strings(struct dsa_switch *ds, int port,
159288
u32 stringset, u8 *data)
160289
{
@@ -185,10 +314,76 @@ static int felix_get_ts_info(struct dsa_switch *ds, int port,
185314
return ocelot_get_ts_info(ocelot, port, info);
186315
}
187316

317+
static int felix_parse_ports_node(struct felix *felix,
318+
struct device_node *ports_node,
319+
phy_interface_t *port_phy_modes)
320+
{
321+
struct ocelot *ocelot = &felix->ocelot;
322+
struct device *dev = felix->ocelot.dev;
323+
struct device_node *child;
324+
325+
for_each_child_of_node(ports_node, child) {
326+
phy_interface_t phy_mode;
327+
u32 port;
328+
int err;
329+
330+
/* Get switch port number from DT */
331+
if (of_property_read_u32(child, "reg", &port) < 0) {
332+
dev_err(dev, "Port number not defined in device tree "
333+
"(property \"reg\")\n");
334+
of_node_put(child);
335+
return -ENODEV;
336+
}
337+
338+
/* Get PHY mode from DT */
339+
err = of_get_phy_mode(child, &phy_mode);
340+
if (err) {
341+
dev_err(dev, "Failed to read phy-mode or "
342+
"phy-interface-type property for port %d\n",
343+
port);
344+
of_node_put(child);
345+
return -ENODEV;
346+
}
347+
348+
err = felix->info->prevalidate_phy_mode(ocelot, port, phy_mode);
349+
if (err < 0) {
350+
dev_err(dev, "Unsupported PHY mode %s on port %d\n",
351+
phy_modes(phy_mode), port);
352+
return err;
353+
}
354+
355+
port_phy_modes[port] = phy_mode;
356+
}
357+
358+
return 0;
359+
}
360+
361+
static int felix_parse_dt(struct felix *felix, phy_interface_t *port_phy_modes)
362+
{
363+
struct device *dev = felix->ocelot.dev;
364+
struct device_node *switch_node;
365+
struct device_node *ports_node;
366+
int err;
367+
368+
switch_node = dev->of_node;
369+
370+
ports_node = of_get_child_by_name(switch_node, "ports");
371+
if (!ports_node) {
372+
dev_err(dev, "Incorrect bindings: absent \"ports\" node\n");
373+
return -ENODEV;
374+
}
375+
376+
err = felix_parse_ports_node(felix, ports_node, port_phy_modes);
377+
of_node_put(ports_node);
378+
379+
return err;
380+
}
381+
188382
static int felix_init_structs(struct felix *felix, int num_phys_ports)
189383
{
190384
struct ocelot *ocelot = &felix->ocelot;
191-
resource_size_t base;
385+
phy_interface_t *port_phy_modes;
386+
resource_size_t switch_base;
192387
int port, i, err;
193388

194389
ocelot->num_phys_ports = num_phys_ports;
@@ -203,7 +398,19 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
203398
ocelot->shared_queue_sz = felix->info->shared_queue_sz;
204399
ocelot->ops = felix->info->ops;
205400

206-
base = pci_resource_start(felix->pdev, felix->info->pci_bar);
401+
port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t),
402+
GFP_KERNEL);
403+
if (!port_phy_modes)
404+
return -ENOMEM;
405+
406+
err = felix_parse_dt(felix, port_phy_modes);
407+
if (err) {
408+
kfree(port_phy_modes);
409+
return err;
410+
}
411+
412+
switch_base = pci_resource_start(felix->pdev,
413+
felix->info->switch_pci_bar);
207414

208415
for (i = 0; i < TARGET_MAX; i++) {
209416
struct regmap *target;
@@ -214,13 +421,14 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
214421

215422
res = &felix->info->target_io_res[i];
216423
res->flags = IORESOURCE_MEM;
217-
res->start += base;
218-
res->end += base;
424+
res->start += switch_base;
425+
res->end += switch_base;
219426

220427
target = ocelot_regmap_init(ocelot, res);
221428
if (IS_ERR(target)) {
222429
dev_err(ocelot->dev,
223430
"Failed to map device memory space\n");
431+
kfree(port_phy_modes);
224432
return PTR_ERR(target);
225433
}
226434

@@ -230,6 +438,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
230438
err = ocelot_regfields_init(ocelot, felix->info->regfields);
231439
if (err) {
232440
dev_err(ocelot->dev, "failed to init reg fields map\n");
441+
kfree(port_phy_modes);
233442
return err;
234443
}
235444

@@ -244,26 +453,37 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
244453
if (!ocelot_port) {
245454
dev_err(ocelot->dev,
246455
"failed to allocate port memory\n");
456+
kfree(port_phy_modes);
247457
return -ENOMEM;
248458
}
249459

250460
res = &felix->info->port_io_res[port];
251461
res->flags = IORESOURCE_MEM;
252-
res->start += base;
253-
res->end += base;
462+
res->start += switch_base;
463+
res->end += switch_base;
254464

255465
port_regs = devm_ioremap_resource(ocelot->dev, res);
256466
if (IS_ERR(port_regs)) {
257467
dev_err(ocelot->dev,
258468
"failed to map registers for port %d\n", port);
469+
kfree(port_phy_modes);
259470
return PTR_ERR(port_regs);
260471
}
261472

473+
ocelot_port->phy_mode = port_phy_modes[port];
262474
ocelot_port->ocelot = ocelot;
263475
ocelot_port->regs = port_regs;
264476
ocelot->ports[port] = ocelot_port;
265477
}
266478

479+
kfree(port_phy_modes);
480+
481+
if (felix->info->mdio_bus_alloc) {
482+
err = felix->info->mdio_bus_alloc(ocelot);
483+
if (err < 0)
484+
return err;
485+
}
486+
267487
return 0;
268488
}
269489

@@ -293,12 +513,22 @@ static int felix_setup(struct dsa_switch *ds)
293513
OCELOT_TAG_PREFIX_LONG);
294514
}
295515

516+
/* It looks like the MAC/PCS interrupt register - PM0_IEVENT (0x8040)
517+
* isn't instantiated for the Felix PF.
518+
* In-band AN may take a few ms to complete, so we need to poll.
519+
*/
520+
ds->pcs_poll = true;
521+
296522
return 0;
297523
}
298524

299525
static void felix_teardown(struct dsa_switch *ds)
300526
{
301527
struct ocelot *ocelot = ds->priv;
528+
struct felix *felix = ocelot_to_felix(ocelot);
529+
530+
if (felix->info->mdio_bus_free)
531+
felix->info->mdio_bus_free(ocelot);
302532

303533
/* stop workqueue thread */
304534
ocelot_deinit(ocelot);
@@ -369,7 +599,12 @@ static const struct dsa_switch_ops felix_switch_ops = {
369599
.get_ethtool_stats = felix_get_ethtool_stats,
370600
.get_sset_count = felix_get_sset_count,
371601
.get_ts_info = felix_get_ts_info,
372-
.adjust_link = felix_adjust_link,
602+
.phylink_validate = felix_phylink_validate,
603+
.phylink_mac_link_state = felix_phylink_mac_pcs_get_state,
604+
.phylink_mac_config = felix_phylink_mac_config,
605+
.phylink_mac_an_restart = felix_phylink_mac_an_restart,
606+
.phylink_mac_link_down = felix_phylink_mac_link_down,
607+
.phylink_mac_link_up = felix_phylink_mac_link_up,
373608
.port_enable = felix_port_enable,
374609
.port_disable = felix_port_disable,
375610
.port_fdb_dump = felix_fdb_dump,

0 commit comments

Comments
 (0)