Skip to content

Commit c8c0ba4

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: felix: setup MMIO filtering rules for PTP when using tag_8021q
Since the tag_8021q tagger is software-defined, it has no means by itself for retrieving hardware timestamps of PTP event messages. Because we do want to support PTP on ocelot even with tag_8021q, we need to use the CPU port module for that. The RX timestamp is present in the Extraction Frame Header. And because we can't use NPI mode which redirects the CPU queues to an "external CPU" (meaning the ARM CPU running Linux), then we need to poll the CPU port module through the MMIO registers to retrieve TX and RX timestamps. Sadly, on NXP LS1028A, the Felix switch was integrated into the SoC without wiring the extraction IRQ line to the ARM GIC. So, if we want to be notified of any PTP packets received on the CPU port module, we have a problem. There is a possible workaround, which is to use the Ethernet CPU port as a notification channel that packets are available on the CPU port module as well. When a PTP packet is received by the DSA tagger (without timestamp, of course), we go to the CPU extraction queues, poll for it there, then we drop the original Ethernet packet and masquerade the packet retrieved over MMIO (plus the timestamp) as the original when we inject it up the stack. Create a quirk in struct felix is selected by the Felix driver (but not by Seville, since that doesn't support PTP at all). We want to do this such that the workaround is minimally invasive for future switches that don't require this workaround. The only traffic for which we need timestamps is PTP traffic, so add a redirection rule to the CPU port module for this. Currently we only have the need for PTP over L2, so redirection rules for UDP ports 319 and 320 are TBD for now. Note that for the workaround of matching of PTP-over-Ethernet-port with PTP-over-MMIO queues to work properly, both channels need to be absolutely lossless. There are two parts to achieving that: - We keep flow control enabled on the tag_8021q CPU port - We put the DSA master interface in promiscuous mode, so it will never drop a PTP frame (for the profiles we are interested in, these are sent to the multicast MAC addresses of 01-80-c2-00-00-0e and 01-1b-19-00-00-00). Signed-off-by: Vladimir Oltean <[email protected]> Reviewed-by: Florian Fainelli <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 924ee31 commit c8c0ba4

File tree

4 files changed

+143
-3
lines changed

4 files changed

+143
-3
lines changed

drivers/net/dsa/ocelot/felix.c

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,120 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port)
264264
ocelot_apply_bridge_fwd_mask(ocelot);
265265
}
266266

267+
/* Set up a VCAP IS2 rule for delivering PTP frames to the CPU port module.
268+
* If the quirk_no_xtr_irq is in place, then also copy those PTP frames to the
269+
* tag_8021q CPU port.
270+
*/
271+
static int felix_setup_mmio_filtering(struct felix *felix)
272+
{
273+
unsigned long user_ports = 0, cpu_ports = 0;
274+
struct ocelot_vcap_filter *redirect_rule;
275+
struct ocelot_vcap_filter *tagging_rule;
276+
struct ocelot *ocelot = &felix->ocelot;
277+
struct dsa_switch *ds = felix->ds;
278+
int port, ret;
279+
280+
tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
281+
if (!tagging_rule)
282+
return -ENOMEM;
283+
284+
redirect_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
285+
if (!redirect_rule) {
286+
kfree(tagging_rule);
287+
return -ENOMEM;
288+
}
289+
290+
for (port = 0; port < ocelot->num_phys_ports; port++) {
291+
if (dsa_is_user_port(ds, port))
292+
user_ports |= BIT(port);
293+
if (dsa_is_cpu_port(ds, port))
294+
cpu_ports |= BIT(port);
295+
}
296+
297+
tagging_rule->key_type = OCELOT_VCAP_KEY_ETYPE;
298+
*(__be16 *)tagging_rule->key.etype.etype.value = htons(ETH_P_1588);
299+
*(__be16 *)tagging_rule->key.etype.etype.mask = htons(0xffff);
300+
tagging_rule->ingress_port_mask = user_ports;
301+
tagging_rule->prio = 1;
302+
tagging_rule->id.cookie = ocelot->num_phys_ports;
303+
tagging_rule->id.tc_offload = false;
304+
tagging_rule->block_id = VCAP_IS1;
305+
tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
306+
tagging_rule->lookup = 0;
307+
tagging_rule->action.pag_override_mask = 0xff;
308+
tagging_rule->action.pag_val = ocelot->num_phys_ports;
309+
310+
ret = ocelot_vcap_filter_add(ocelot, tagging_rule, NULL);
311+
if (ret) {
312+
kfree(tagging_rule);
313+
kfree(redirect_rule);
314+
return ret;
315+
}
316+
317+
redirect_rule->key_type = OCELOT_VCAP_KEY_ANY;
318+
redirect_rule->ingress_port_mask = user_ports;
319+
redirect_rule->pag = ocelot->num_phys_ports;
320+
redirect_rule->prio = 1;
321+
redirect_rule->id.cookie = ocelot->num_phys_ports;
322+
redirect_rule->id.tc_offload = false;
323+
redirect_rule->block_id = VCAP_IS2;
324+
redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
325+
redirect_rule->lookup = 0;
326+
redirect_rule->action.cpu_copy_ena = true;
327+
if (felix->info->quirk_no_xtr_irq) {
328+
/* Redirect to the tag_8021q CPU but also copy PTP packets to
329+
* the CPU port module
330+
*/
331+
redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
332+
redirect_rule->action.port_mask = cpu_ports;
333+
} else {
334+
/* Trap PTP packets only to the CPU port module (which is
335+
* redirected to the NPI port)
336+
*/
337+
redirect_rule->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
338+
redirect_rule->action.port_mask = 0;
339+
}
340+
341+
ret = ocelot_vcap_filter_add(ocelot, redirect_rule, NULL);
342+
if (ret) {
343+
ocelot_vcap_filter_del(ocelot, tagging_rule);
344+
kfree(redirect_rule);
345+
return ret;
346+
}
347+
348+
return 0;
349+
}
350+
351+
static int felix_teardown_mmio_filtering(struct felix *felix)
352+
{
353+
struct ocelot_vcap_filter *tagging_rule, *redirect_rule;
354+
struct ocelot_vcap_block *block_vcap_is1;
355+
struct ocelot_vcap_block *block_vcap_is2;
356+
struct ocelot *ocelot = &felix->ocelot;
357+
int err;
358+
359+
block_vcap_is1 = &ocelot->block[VCAP_IS1];
360+
block_vcap_is2 = &ocelot->block[VCAP_IS2];
361+
362+
tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1,
363+
ocelot->num_phys_ports,
364+
false);
365+
if (!tagging_rule)
366+
return -ENOENT;
367+
368+
err = ocelot_vcap_filter_del(ocelot, tagging_rule);
369+
if (err)
370+
return err;
371+
372+
redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2,
373+
ocelot->num_phys_ports,
374+
false);
375+
if (!redirect_rule)
376+
return -ENOENT;
377+
378+
return ocelot_vcap_filter_del(ocelot, redirect_rule);
379+
}
380+
267381
static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
268382
{
269383
struct ocelot *ocelot = ds->priv;
@@ -292,9 +406,9 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
292406
ANA_PORT_CPU_FWD_BPDU_CFG, port);
293407
}
294408

295-
/* In tag_8021q mode, the CPU port module is unused. So we
296-
* want to disable flooding of any kind to the CPU port module,
297-
* since packets going there will end in a black hole.
409+
/* In tag_8021q mode, the CPU port module is unused, except for PTP
410+
* frames. So we want to disable flooding of any kind to the CPU port
411+
* module, since packets going there will end in a black hole.
298412
*/
299413
cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports));
300414
ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_UC);
@@ -314,8 +428,14 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
314428
if (err)
315429
goto out_free_dsa_8021_ctx;
316430

431+
err = felix_setup_mmio_filtering(felix);
432+
if (err)
433+
goto out_teardown_dsa_8021q;
434+
317435
return 0;
318436

437+
out_teardown_dsa_8021q:
438+
dsa_8021q_setup(felix->dsa_8021q_ctx, false);
319439
out_free_dsa_8021_ctx:
320440
kfree(felix->dsa_8021q_ctx);
321441
return err;
@@ -327,6 +447,11 @@ static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu)
327447
struct felix *felix = ocelot_to_felix(ocelot);
328448
int err, port;
329449

450+
err = felix_teardown_mmio_filtering(felix);
451+
if (err)
452+
dev_err(ds->dev, "felix_teardown_mmio_filtering returned %d",
453+
err);
454+
330455
err = dsa_8021q_setup(felix->dsa_8021q_ctx, false);
331456
if (err)
332457
dev_err(ds->dev, "dsa_8021q_setup returned %d", err);

drivers/net/dsa/ocelot/felix.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ struct felix_info {
2323
int switch_pci_bar;
2424
int imdio_pci_bar;
2525
const struct ptp_clock_info *ptp_caps;
26+
27+
/* Some Ocelot switches are integrated into the SoC without the
28+
* extraction IRQ line connected to the ARM GIC. By enabling this
29+
* workaround, the few packets that are delivered to the CPU port
30+
* module (currently only PTP) are copied not only to the hardware CPU
31+
* port module, but also to the 802.1Q Ethernet CPU port, and polling
32+
* the extraction registers is triggered once the DSA tagger sees a PTP
33+
* frame. The Ethernet frame is only used as a notification: it is
34+
* dropped, and the original frame is extracted over MMIO and annotated
35+
* with the RX timestamp.
36+
*/
37+
bool quirk_no_xtr_irq;
38+
2639
int (*mdio_bus_alloc)(struct ocelot *ocelot);
2740
void (*mdio_bus_free)(struct ocelot *ocelot);
2841
void (*phylink_validate)(struct ocelot *ocelot, int port,

drivers/net/dsa/ocelot/felix_vsc9959.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,7 @@ static const struct felix_info felix_info_vsc9959 = {
13541354
.num_tx_queues = OCELOT_NUM_TC,
13551355
.switch_pci_bar = 4,
13561356
.imdio_pci_bar = 0,
1357+
.quirk_no_xtr_irq = true,
13571358
.ptp_caps = &vsc9959_ptp_caps,
13581359
.mdio_bus_alloc = vsc9959_mdio_bus_alloc,
13591360
.mdio_bus_free = vsc9959_mdio_bus_free,

net/dsa/tag_ocelot_8021q.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ static const struct dsa_device_ops ocelot_8021q_netdev_ops = {
6060
.xmit = ocelot_xmit,
6161
.rcv = ocelot_rcv,
6262
.overhead = VLAN_HLEN,
63+
.promisc_on_master = true,
6364
};
6465

6566
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)