Commit c48f8607 authored by David S. Miller's avatar David S. Miller

Merge branch 'PTP-for-DSA-tag_ocelot_8021q'

Vladimir Oltean says:

====================
PTP for DSA tag_ocelot_8021q

Changes in v2:
Add stub definition for ocelot_port_inject_frame when switch driver is
not compiled in.

This is part two of the errata workaround begun here:
https://patchwork.kernel.org/project/netdevbpf/cover/20210129010009.3959398-1-olteanv@gmail.com/

Now that we have basic traffic support when we operate the Ocelot DSA
switches without an NPI port, it would be nice to regain some of the
features lost due to the lack of the NPI port functionality. An
important one is PTP timestamping, which is intimately tied to the DSA
frame header added by the NPI port: on TX, we put a "timestamp request
ID" in the Injection Frame Header, while on RX, the Extraction Frame
Header contains a partial 32-bit PTP timestamp. Get rid of the NPI port
and replace it with a VLAN-based tagger, and you lose PTP, right?

Well, not quite, this is what this patch series is about. The NPI port
is basically a regular Ethernet port configured to service the packets
in and out of the switch's CPU port module (which has other non-DSA I/O
mechanisms too, such as register-based MMIO and DMA). If we disable the
NPI port, we can in theory still access the packets delivered to the CPU
port module by doing exactly what the ocelot switchdev driver does:
extracting Ethernet packets through registers (yes, it is as icky as it
sounds).

However, there's a catch. The Felix switch was integrated into NXP
LS1028A with the idea in mind that it will operate as DSA, i.e. using
the CPU port module connected to the NPI port, not having I/O over
register-based MMIO which is painfully slow and CPU intensive. So
register-based packet I/O not supposed to work - those registers aren't
even documented in the hardware reference manual for Felix. However
they kinda do, with the exception of the fact that an RX interrupt was
really not wired to the CPU cores - so we don't know when the CPU port
module receives a new packet. But we can hack even around that, by
replicating every packet that goes to the CPU port module and making it
also go to a plain internal Ethernet port. Then drop the Ethernet packet
and read the other copy of it from the CPU port module, this time
annotated with the much-wanted RX timestamp.

This is all fine and it works, but it does raise some questions about
what DSA even is anymore, if we start having switches that inject some
of their packets over Ethernet and some through registers, where do we
draw the line. In principle I believe these concerns are founded, but at
the same time, the way that the Felix driver uses register MMIO based
packet I/O is fundamentally the same as any other DSA driver capable of
PTP makes use of a side-channel for timestamps like a FIFO (just that
this one is a lot more complicated, and comes with the entire actual
packet, not just the timestamp).

Nonetheless, I tried to keep the extra pressure added by this ERR
workaround upon the DSA subsystem as small as possible, so some of the
patches are just a revisit of some of Andrew's complaints w.r.t. the
fact that tag_ocelot already violates any driver <-> tagger boundary,
and as a consequence, is not able to be used on testbeds such as
dsa_loop (which it now can). So now, the tag_ocelot and tag_ocelot_8021q
drivers should be dsa_loop-clean, and have the ERR workarounds as
self-contained as possible, using all the designated features for PTP
timestamping and nothing more.

Comments appreciated.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 14026192 0a6f17c6
...@@ -14,8 +14,9 @@ ...@@ -14,8 +14,9 @@
#include <soc/mscc/ocelot_ptp.h> #include <soc/mscc/ocelot_ptp.h>
#include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot.h>
#include <linux/dsa/8021q.h> #include <linux/dsa/8021q.h>
#include <linux/dsa/ocelot.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/packing.h> #include <linux/ptp_classify.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_net.h> #include <linux/of_net.h>
#include <linux/pci.h> #include <linux/pci.h>
...@@ -264,6 +265,129 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port) ...@@ -264,6 +265,129 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port)
ocelot_apply_bridge_fwd_mask(ocelot); ocelot_apply_bridge_fwd_mask(ocelot);
} }
/* Set up a VCAP IS2 rule for delivering PTP frames to the CPU port module.
* If the quirk_no_xtr_irq is in place, then also copy those PTP frames to the
* tag_8021q CPU port.
*/
static int felix_setup_mmio_filtering(struct felix *felix)
{
unsigned long user_ports = 0, cpu_ports = 0;
struct ocelot_vcap_filter *redirect_rule;
struct ocelot_vcap_filter *tagging_rule;
struct ocelot *ocelot = &felix->ocelot;
struct dsa_switch *ds = felix->ds;
int port, ret;
tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
if (!tagging_rule)
return -ENOMEM;
redirect_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
if (!redirect_rule) {
kfree(tagging_rule);
return -ENOMEM;
}
for (port = 0; port < ocelot->num_phys_ports; port++) {
if (dsa_is_user_port(ds, port))
user_ports |= BIT(port);
if (dsa_is_cpu_port(ds, port))
cpu_ports |= BIT(port);
}
tagging_rule->key_type = OCELOT_VCAP_KEY_ETYPE;
*(__be16 *)tagging_rule->key.etype.etype.value = htons(ETH_P_1588);
*(__be16 *)tagging_rule->key.etype.etype.mask = htons(0xffff);
tagging_rule->ingress_port_mask = user_ports;
tagging_rule->prio = 1;
tagging_rule->id.cookie = ocelot->num_phys_ports;
tagging_rule->id.tc_offload = false;
tagging_rule->block_id = VCAP_IS1;
tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
tagging_rule->lookup = 0;
tagging_rule->action.pag_override_mask = 0xff;
tagging_rule->action.pag_val = ocelot->num_phys_ports;
ret = ocelot_vcap_filter_add(ocelot, tagging_rule, NULL);
if (ret) {
kfree(tagging_rule);
kfree(redirect_rule);
return ret;
}
redirect_rule->key_type = OCELOT_VCAP_KEY_ANY;
redirect_rule->ingress_port_mask = user_ports;
redirect_rule->pag = ocelot->num_phys_ports;
redirect_rule->prio = 1;
redirect_rule->id.cookie = ocelot->num_phys_ports;
redirect_rule->id.tc_offload = false;
redirect_rule->block_id = VCAP_IS2;
redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
redirect_rule->lookup = 0;
redirect_rule->action.cpu_copy_ena = true;
if (felix->info->quirk_no_xtr_irq) {
/* Redirect to the tag_8021q CPU but also copy PTP packets to
* the CPU port module
*/
redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
redirect_rule->action.port_mask = cpu_ports;
} else {
/* Trap PTP packets only to the CPU port module (which is
* redirected to the NPI port)
*/
redirect_rule->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
redirect_rule->action.port_mask = 0;
}
ret = ocelot_vcap_filter_add(ocelot, redirect_rule, NULL);
if (ret) {
ocelot_vcap_filter_del(ocelot, tagging_rule);
kfree(redirect_rule);
return ret;
}
/* The ownership of the CPU port module's queues might have just been
* transferred to the tag_8021q tagger from the NPI-based tagger.
* So there might still be all sorts of crap in the queues. On the
* other hand, the MMIO-based matching of PTP frames is very brittle,
* so we need to be careful that there are no extra frames to be
* dequeued over MMIO, since we would never know to discard them.
*/
ocelot_drain_cpu_queue(ocelot, 0);
return 0;
}
static int felix_teardown_mmio_filtering(struct felix *felix)
{
struct ocelot_vcap_filter *tagging_rule, *redirect_rule;
struct ocelot_vcap_block *block_vcap_is1;
struct ocelot_vcap_block *block_vcap_is2;
struct ocelot *ocelot = &felix->ocelot;
int err;
block_vcap_is1 = &ocelot->block[VCAP_IS1];
block_vcap_is2 = &ocelot->block[VCAP_IS2];
tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1,
ocelot->num_phys_ports,
false);
if (!tagging_rule)
return -ENOENT;
err = ocelot_vcap_filter_del(ocelot, tagging_rule);
if (err)
return err;
redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2,
ocelot->num_phys_ports,
false);
if (!redirect_rule)
return -ENOENT;
return ocelot_vcap_filter_del(ocelot, redirect_rule);
}
static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
{ {
struct ocelot *ocelot = ds->priv; struct ocelot *ocelot = ds->priv;
...@@ -292,9 +416,9 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) ...@@ -292,9 +416,9 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
ANA_PORT_CPU_FWD_BPDU_CFG, port); ANA_PORT_CPU_FWD_BPDU_CFG, port);
} }
/* In tag_8021q mode, the CPU port module is unused. So we /* In tag_8021q mode, the CPU port module is unused, except for PTP
* want to disable flooding of any kind to the CPU port module, * frames. So we want to disable flooding of any kind to the CPU port
* since packets going there will end in a black hole. * module, since packets going there will end in a black hole.
*/ */
cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)); cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports));
ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_UC); ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_UC);
...@@ -314,8 +438,14 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) ...@@ -314,8 +438,14 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
if (err) if (err)
goto out_free_dsa_8021_ctx; goto out_free_dsa_8021_ctx;
err = felix_setup_mmio_filtering(felix);
if (err)
goto out_teardown_dsa_8021q;
return 0; return 0;
out_teardown_dsa_8021q:
dsa_8021q_setup(felix->dsa_8021q_ctx, false);
out_free_dsa_8021_ctx: out_free_dsa_8021_ctx:
kfree(felix->dsa_8021q_ctx); kfree(felix->dsa_8021q_ctx);
return err; return err;
...@@ -327,6 +457,11 @@ static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu) ...@@ -327,6 +457,11 @@ static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu)
struct felix *felix = ocelot_to_felix(ocelot); struct felix *felix = ocelot_to_felix(ocelot);
int err, port; int err, port;
err = felix_teardown_mmio_filtering(felix);
if (err)
dev_err(ds->dev, "felix_teardown_mmio_filtering returned %d",
err);
err = dsa_8021q_setup(felix->dsa_8021q_ctx, false); err = dsa_8021q_setup(felix->dsa_8021q_ctx, false);
if (err) if (err)
dev_err(ds->dev, "dsa_8021q_setup returned %d", err); dev_err(ds->dev, "dsa_8021q_setup returned %d", err);
...@@ -431,6 +566,7 @@ static int felix_set_tag_protocol(struct dsa_switch *ds, int cpu, ...@@ -431,6 +566,7 @@ static int felix_set_tag_protocol(struct dsa_switch *ds, int cpu,
int err; int err;
switch (proto) { switch (proto) {
case DSA_TAG_PROTO_SEVILLE:
case DSA_TAG_PROTO_OCELOT: case DSA_TAG_PROTO_OCELOT:
err = felix_setup_tag_npi(ds, cpu); err = felix_setup_tag_npi(ds, cpu);
break; break;
...@@ -448,6 +584,7 @@ static void felix_del_tag_protocol(struct dsa_switch *ds, int cpu, ...@@ -448,6 +584,7 @@ static void felix_del_tag_protocol(struct dsa_switch *ds, int cpu,
enum dsa_tag_protocol proto) enum dsa_tag_protocol proto)
{ {
switch (proto) { switch (proto) {
case DSA_TAG_PROTO_SEVILLE:
case DSA_TAG_PROTO_OCELOT: case DSA_TAG_PROTO_OCELOT:
felix_teardown_tag_npi(ds, cpu); felix_teardown_tag_npi(ds, cpu);
break; break;
...@@ -471,7 +608,8 @@ static int felix_change_tag_protocol(struct dsa_switch *ds, int cpu, ...@@ -471,7 +608,8 @@ static int felix_change_tag_protocol(struct dsa_switch *ds, int cpu,
enum dsa_tag_protocol old_proto = felix->tag_proto; enum dsa_tag_protocol old_proto = felix->tag_proto;
int err; int err;
if (proto != DSA_TAG_PROTO_OCELOT && if (proto != DSA_TAG_PROTO_SEVILLE &&
proto != DSA_TAG_PROTO_OCELOT &&
proto != DSA_TAG_PROTO_OCELOT_8021Q) proto != DSA_TAG_PROTO_OCELOT_8021Q)
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
...@@ -1003,7 +1141,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) ...@@ -1003,7 +1141,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
for (port = 0; port < num_phys_ports; port++) { for (port = 0; port < num_phys_ports; port++) {
struct ocelot_port *ocelot_port; struct ocelot_port *ocelot_port;
struct regmap *target; struct regmap *target;
u8 *template;
ocelot_port = devm_kzalloc(ocelot->dev, ocelot_port = devm_kzalloc(ocelot->dev,
sizeof(struct ocelot_port), sizeof(struct ocelot_port),
...@@ -1029,22 +1166,10 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) ...@@ -1029,22 +1166,10 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
return PTR_ERR(target); return PTR_ERR(target);
} }
template = devm_kzalloc(ocelot->dev, OCELOT_TOTAL_TAG_LEN,
GFP_KERNEL);
if (!template) {
dev_err(ocelot->dev,
"Failed to allocate memory for DSA tag\n");
kfree(port_phy_modes);
return -ENOMEM;
}
ocelot_port->phy_mode = port_phy_modes[port]; ocelot_port->phy_mode = port_phy_modes[port];
ocelot_port->ocelot = ocelot; ocelot_port->ocelot = ocelot;
ocelot_port->target = target; ocelot_port->target = target;
ocelot_port->xmit_template = template;
ocelot->ports[port] = ocelot_port; ocelot->ports[port] = ocelot_port;
felix->info->xmit_template_populate(ocelot, port);
} }
kfree(port_phy_modes); kfree(port_phy_modes);
...@@ -1158,20 +1283,79 @@ static int felix_hwtstamp_set(struct dsa_switch *ds, int port, ...@@ -1158,20 +1283,79 @@ static int felix_hwtstamp_set(struct dsa_switch *ds, int port,
return ocelot_hwstamp_set(ocelot, port, ifr); return ocelot_hwstamp_set(ocelot, port, ifr);
} }
static bool felix_check_xtr_pkt(struct ocelot *ocelot, unsigned int ptp_type)
{
struct felix *felix = ocelot_to_felix(ocelot);
int err, grp = 0;
if (felix->tag_proto != DSA_TAG_PROTO_OCELOT_8021Q)
return false;
if (!felix->info->quirk_no_xtr_irq)
return false;
if (ptp_type == PTP_CLASS_NONE)
return false;
while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) {
struct sk_buff *skb;
unsigned int type;
err = ocelot_xtr_poll_frame(ocelot, grp, &skb);
if (err)
goto out;
/* We trap to the CPU port module all PTP frames, but
* felix_rxtstamp() only gets called for event frames.
* So we need to avoid sending duplicate general
* message frames by running a second BPF classifier
* here and dropping those.
*/
__skb_push(skb, ETH_HLEN);
type = ptp_classify_raw(skb);
__skb_pull(skb, ETH_HLEN);
if (type == PTP_CLASS_NONE) {
kfree_skb(skb);
continue;
}
netif_rx(skb);
}
out:
if (err < 0)
ocelot_drain_cpu_queue(ocelot, 0);
return true;
}
static bool felix_rxtstamp(struct dsa_switch *ds, int port, static bool felix_rxtstamp(struct dsa_switch *ds, int port,
struct sk_buff *skb, unsigned int type) struct sk_buff *skb, unsigned int type)
{ {
u8 *extraction = skb->data - ETH_HLEN - OCELOT_TAG_LEN;
struct skb_shared_hwtstamps *shhwtstamps; struct skb_shared_hwtstamps *shhwtstamps;
struct ocelot *ocelot = ds->priv; struct ocelot *ocelot = ds->priv;
u8 *extraction = skb->data - ETH_HLEN - OCELOT_TAG_LEN;
u32 tstamp_lo, tstamp_hi; u32 tstamp_lo, tstamp_hi;
struct timespec64 ts; struct timespec64 ts;
u64 tstamp, val; u64 tstamp, val;
/* If the "no XTR IRQ" workaround is in use, tell DSA to defer this skb
* for RX timestamping. Then free it, and poll for its copy through
* MMIO in the CPU port module, and inject that into the stack from
* ocelot_xtr_poll().
*/
if (felix_check_xtr_pkt(ocelot, type)) {
kfree_skb(skb);
return true;
}
ocelot_ptp_gettime64(&ocelot->ptp_info, &ts); ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
tstamp = ktime_set(ts.tv_sec, ts.tv_nsec); tstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
packing(extraction, &val, 116, 85, OCELOT_TAG_LEN, UNPACK, 0); ocelot_xfh_get_rew_val(extraction, &val);
tstamp_lo = (u32)val; tstamp_lo = (u32)val;
tstamp_hi = tstamp >> 32; tstamp_hi = tstamp >> 32;
......
...@@ -23,6 +23,19 @@ struct felix_info { ...@@ -23,6 +23,19 @@ struct felix_info {
int switch_pci_bar; int switch_pci_bar;
int imdio_pci_bar; int imdio_pci_bar;
const struct ptp_clock_info *ptp_caps; const struct ptp_clock_info *ptp_caps;
/* Some Ocelot switches are integrated into the SoC without the
* extraction IRQ line connected to the ARM GIC. By enabling this
* workaround, the few packets that are delivered to the CPU port
* module (currently only PTP) are copied not only to the hardware CPU
* port module, but also to the 802.1Q Ethernet CPU port, and polling
* the extraction registers is triggered once the DSA tagger sees a PTP
* frame. The Ethernet frame is only used as a notification: it is
* dropped, and the original frame is extracted over MMIO and annotated
* with the RX timestamp.
*/
bool quirk_no_xtr_irq;
int (*mdio_bus_alloc)(struct ocelot *ocelot); int (*mdio_bus_alloc)(struct ocelot *ocelot);
void (*mdio_bus_free)(struct ocelot *ocelot); void (*mdio_bus_free)(struct ocelot *ocelot);
void (*phylink_validate)(struct ocelot *ocelot, int port, void (*phylink_validate)(struct ocelot *ocelot, int port,
...@@ -34,7 +47,6 @@ struct felix_info { ...@@ -34,7 +47,6 @@ struct felix_info {
enum tc_setup_type type, void *type_data); enum tc_setup_type type, void *type_data);
void (*port_sched_speed_set)(struct ocelot *ocelot, int port, void (*port_sched_speed_set)(struct ocelot *ocelot, int port,
u32 speed); u32 speed);
void (*xmit_template_populate)(struct ocelot *ocelot, int port);
}; };
extern const struct dsa_switch_ops felix_switch_ops; extern const struct dsa_switch_ops felix_switch_ops;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include <soc/mscc/ocelot_ptp.h> #include <soc/mscc/ocelot_ptp.h>
#include <soc/mscc/ocelot_sys.h> #include <soc/mscc/ocelot_sys.h>
#include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot.h>
#include <linux/packing.h> #include <linux/dsa/ocelot.h>
#include <linux/pcs-lynx.h> #include <linux/pcs-lynx.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
...@@ -1339,31 +1339,6 @@ static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port, ...@@ -1339,31 +1339,6 @@ static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port,
} }
} }
static void vsc9959_xmit_template_populate(struct ocelot *ocelot, int port)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u8 *template = ocelot_port->xmit_template;
u64 bypass, dest, src;
__be32 *prefix;
u8 *injection;
/* Set the source port as the CPU port module and not the
* NPI port
*/
src = ocelot->num_phys_ports;
dest = BIT(port);
bypass = true;
injection = template + OCELOT_SHORT_PREFIX_LEN;
prefix = (__be32 *)template;
packing(injection, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0);
packing(injection, &dest, 68, 56, OCELOT_TAG_LEN, PACK, 0);
packing(injection, &src, 46, 43, OCELOT_TAG_LEN, PACK, 0);
*prefix = cpu_to_be32(0x8880000a);
}
static const struct felix_info felix_info_vsc9959 = { static const struct felix_info felix_info_vsc9959 = {
.target_io_res = vsc9959_target_io_res, .target_io_res = vsc9959_target_io_res,
.port_io_res = vsc9959_port_io_res, .port_io_res = vsc9959_port_io_res,
...@@ -1379,6 +1354,7 @@ static const struct felix_info felix_info_vsc9959 = { ...@@ -1379,6 +1354,7 @@ static const struct felix_info felix_info_vsc9959 = {
.num_tx_queues = OCELOT_NUM_TC, .num_tx_queues = OCELOT_NUM_TC,
.switch_pci_bar = 4, .switch_pci_bar = 4,
.imdio_pci_bar = 0, .imdio_pci_bar = 0,
.quirk_no_xtr_irq = true,
.ptp_caps = &vsc9959_ptp_caps, .ptp_caps = &vsc9959_ptp_caps,
.mdio_bus_alloc = vsc9959_mdio_bus_alloc, .mdio_bus_alloc = vsc9959_mdio_bus_alloc,
.mdio_bus_free = vsc9959_mdio_bus_free, .mdio_bus_free = vsc9959_mdio_bus_free,
...@@ -1386,7 +1362,6 @@ static const struct felix_info felix_info_vsc9959 = { ...@@ -1386,7 +1362,6 @@ static const struct felix_info felix_info_vsc9959 = {
.prevalidate_phy_mode = vsc9959_prevalidate_phy_mode, .prevalidate_phy_mode = vsc9959_prevalidate_phy_mode,
.port_setup_tc = vsc9959_port_setup_tc, .port_setup_tc = vsc9959_port_setup_tc,
.port_sched_speed_set = vsc9959_sched_speed_set, .port_sched_speed_set = vsc9959_sched_speed_set,
.xmit_template_populate = vsc9959_xmit_template_populate,
}; };
static irqreturn_t felix_irq_handler(int irq, void *data) static irqreturn_t felix_irq_handler(int irq, void *data)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/pcs-lynx.h> #include <linux/pcs-lynx.h>
#include <linux/packing.h> #include <linux/dsa/ocelot.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include "felix.h" #include "felix.h"
...@@ -1165,31 +1165,6 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot) ...@@ -1165,31 +1165,6 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot)
mdiobus_unregister(felix->imdio); mdiobus_unregister(felix->imdio);
} }
static void vsc9953_xmit_template_populate(struct ocelot *ocelot, int port)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u8 *template = ocelot_port->xmit_template;
u64 bypass, dest, src;
__be32 *prefix;
u8 *injection;
/* Set the source port as the CPU port module and not the
* NPI port
*/
src = ocelot->num_phys_ports;
dest = BIT(port);
bypass = true;
injection = template + OCELOT_SHORT_PREFIX_LEN;
prefix = (__be32 *)template;
packing(injection, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0);
packing(injection, &dest, 67, 57, OCELOT_TAG_LEN, PACK, 0);
packing(injection, &src, 46, 43, OCELOT_TAG_LEN, PACK, 0);
*prefix = cpu_to_be32(0x88800005);
}
static const struct felix_info seville_info_vsc9953 = { static const struct felix_info seville_info_vsc9953 = {
.target_io_res = vsc9953_target_io_res, .target_io_res = vsc9953_target_io_res,
.port_io_res = vsc9953_port_io_res, .port_io_res = vsc9953_port_io_res,
...@@ -1206,7 +1181,6 @@ static const struct felix_info seville_info_vsc9953 = { ...@@ -1206,7 +1181,6 @@ static const struct felix_info seville_info_vsc9953 = {
.mdio_bus_free = vsc9953_mdio_bus_free, .mdio_bus_free = vsc9953_mdio_bus_free,
.phylink_validate = vsc9953_phylink_validate, .phylink_validate = vsc9953_phylink_validate,
.prevalidate_phy_mode = vsc9953_prevalidate_phy_mode, .prevalidate_phy_mode = vsc9953_prevalidate_phy_mode,
.xmit_template_populate = vsc9953_xmit_template_populate,
}; };
static int seville_probe(struct platform_device *pdev) static int seville_probe(struct platform_device *pdev)
...@@ -1246,7 +1220,7 @@ static int seville_probe(struct platform_device *pdev) ...@@ -1246,7 +1220,7 @@ static int seville_probe(struct platform_device *pdev)
ds->ops = &felix_switch_ops; ds->ops = &felix_switch_ops;
ds->priv = ocelot; ds->priv = ocelot;
felix->ds = ds; felix->ds = ds;
felix->tag_proto = DSA_TAG_PROTO_OCELOT; felix->tag_proto = DSA_TAG_PROTO_SEVILLE;
err = dsa_register_switch(ds); err = dsa_register_switch(ds);
if (err) { if (err) {
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* *
* Copyright (c) 2017 Microsemi Corporation * Copyright (c) 2017 Microsemi Corporation
*/ */
#include <linux/dsa/ocelot.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <soc/mscc/ocelot_vcap.h> #include <soc/mscc/ocelot_vcap.h>
#include "ocelot.h" #include "ocelot.h"
...@@ -628,6 +629,221 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) ...@@ -628,6 +629,221 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
} }
EXPORT_SYMBOL(ocelot_get_txtstamp); EXPORT_SYMBOL(ocelot_get_txtstamp);
static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh,
u32 *rval)
{
u32 bytes_valid, val;
val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
if (val == XTR_NOT_READY) {
if (ifh)
return -EIO;
do {
val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
} while (val == XTR_NOT_READY);
}
switch (val) {
case XTR_ABORT:
return -EIO;
case XTR_EOF_0:
case XTR_EOF_1:
case XTR_EOF_2:
case XTR_EOF_3:
case XTR_PRUNED:
bytes_valid = XTR_VALID_BYTES(val);
val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
if (val == XTR_ESCAPE)
*rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
else
*rval = val;
return bytes_valid;
case XTR_ESCAPE:
*rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
return 4;
default:
*rval = val;
return 4;
}
}
static int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp, u32 *xfh)
{
int i, err = 0;
for (i = 0; i < OCELOT_TAG_LEN / 4; i++) {
err = ocelot_rx_frame_word(ocelot, grp, true, &xfh[i]);
if (err != 4)
return (err < 0) ? err : -EIO;
}
return 0;
}
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
{
struct skb_shared_hwtstamps *shhwtstamps;
u64 tod_in_ns, full_ts_in_ns;
u64 timestamp, src_port, len;
u32 xfh[OCELOT_TAG_LEN / 4];
struct net_device *dev;
struct timespec64 ts;
struct sk_buff *skb;
int sz, buf_len;
u32 val, *buf;
int err;
err = ocelot_xtr_poll_xfh(ocelot, grp, xfh);
if (err)
return err;
ocelot_xfh_get_src_port(xfh, &src_port);
ocelot_xfh_get_len(xfh, &len);
ocelot_xfh_get_rew_val(xfh, &timestamp);
if (WARN_ON(src_port >= ocelot->num_phys_ports))
return -EINVAL;
dev = ocelot->ops->port_to_netdev(ocelot, src_port);
if (!dev)
return -EINVAL;
skb = netdev_alloc_skb(dev, len);
if (unlikely(!skb)) {
netdev_err(dev, "Unable to allocate sk_buff\n");
return -ENOMEM;
}
buf_len = len - ETH_FCS_LEN;
buf = (u32 *)skb_put(skb, buf_len);
len = 0;
do {
sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
if (sz < 0) {
err = sz;
goto out_free_skb;
}
*buf++ = val;
len += sz;
} while (len < buf_len);
/* Read the FCS */
sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
if (sz < 0) {
err = sz;
goto out_free_skb;
}
/* Update the statistics if part of the FCS was read before */
len -= ETH_FCS_LEN - sz;
if (unlikely(dev->features & NETIF_F_RXFCS)) {
buf = (u32 *)skb_put(skb, ETH_FCS_LEN);
*buf = val;
}
if (ocelot->ptp) {
ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec);
if ((tod_in_ns & 0xffffffff) < timestamp)
full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) |
timestamp;
else
full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) |
timestamp;
shhwtstamps = skb_hwtstamps(skb);
memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
shhwtstamps->hwtstamp = full_ts_in_ns;
}
/* Everything we see on an interface that is in the HW bridge
* has already been forwarded.
*/
if (ocelot->bridge_mask & BIT(src_port))
skb->offload_fwd_mark = 1;
skb->protocol = eth_type_trans(skb, dev);
*nskb = skb;
return 0;
out_free_skb:
kfree_skb(skb);
return err;
}
EXPORT_SYMBOL(ocelot_xtr_poll_frame);
bool ocelot_can_inject(struct ocelot *ocelot, int grp)
{
u32 val = ocelot_read(ocelot, QS_INJ_STATUS);
if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))))
return false;
if (val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp)))
return false;
return true;
}
EXPORT_SYMBOL(ocelot_can_inject);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb)
{
u32 ifh[OCELOT_TAG_LEN / 4] = {0};
unsigned int i, count, last;
ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);
ocelot_ifh_set_bypass(ifh, 1);
ocelot_ifh_set_dest(ifh, BIT(port));
ocelot_ifh_set_tag_type(ifh, IFH_TAG_TYPE_C);
ocelot_ifh_set_vid(ifh, skb_vlan_tag_get(skb));
ocelot_ifh_set_rew_op(ifh, rew_op);
for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
ocelot_write_rix(ocelot, ifh[i], QS_INJ_WR, grp);
count = DIV_ROUND_UP(skb->len, 4);
last = skb->len % 4;
for (i = 0; i < count; i++)
ocelot_write_rix(ocelot, ((u32 *)skb->data)[i], QS_INJ_WR, grp);
/* Add padding */
while (i < (OCELOT_BUFFER_CELL_SZ / 4)) {
ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
i++;
}
/* Indicate EOF and valid bytes in last word */
ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
QS_INJ_CTRL_VLD_BYTES(skb->len < OCELOT_BUFFER_CELL_SZ ? 0 : last) |
QS_INJ_CTRL_EOF,
QS_INJ_CTRL, grp);
/* Add dummy CRC */
ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
skb_tx_timestamp(skb);
skb->dev->stats.tx_packets++;
skb->dev->stats.tx_bytes += skb->len;
}
EXPORT_SYMBOL(ocelot_port_inject_frame);
void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp)
{
while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))
ocelot_read_rix(ocelot, QS_XTR_RD, grp);
}
EXPORT_SYMBOL(ocelot_drain_cpu_queue);
int ocelot_fdb_add(struct ocelot *ocelot, int port, int ocelot_fdb_add(struct ocelot *ocelot, int port,
const unsigned char *addr, u16 vid) const unsigned char *addr, u16 vid)
{ {
......
...@@ -32,15 +32,6 @@ ...@@ -32,15 +32,6 @@
#define OCELOT_PTP_QUEUE_SZ 128 #define OCELOT_PTP_QUEUE_SZ 128
struct frame_info {
u32 len;
u16 port;
u16 vid;
u8 tag_type;
u16 rew_op;
u32 timestamp; /* rew_val */
};
struct ocelot_port_tc { struct ocelot_port_tc {
bool block_shared; bool block_shared;
unsigned long offload_cnt; unsigned long offload_cnt;
......
...@@ -488,53 +488,20 @@ static int ocelot_port_stop(struct net_device *dev) ...@@ -488,53 +488,20 @@ static int ocelot_port_stop(struct net_device *dev)
return 0; return 0;
} }
/* Generate the IFH for frame injection static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
*
* The IFH is a 128bit-value
* bit 127: bypass the analyzer processing
* bit 56-67: destination mask
* bit 28-29: pop_cnt: 3 disables all rewriting of the frame
* bit 20-27: cpu extraction queue mask
* bit 16: tag type 0: C-tag, 1: S-tag
* bit 0-11: VID
*/
static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info)
{
ifh[0] = IFH_INJ_BYPASS | ((0x1ff & info->rew_op) << 21);
ifh[1] = (0xf00 & info->port) >> 8;
ifh[2] = (0xff & info->port) << 24;
ifh[3] = (info->tag_type << 16) | info->vid;
return 0;
}
static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct skb_shared_info *shinfo = skb_shinfo(skb);
struct ocelot_port *ocelot_port = &priv->port; struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot; struct ocelot *ocelot = ocelot_port->ocelot;
u32 val, ifh[OCELOT_TAG_LEN / 4];
struct frame_info info = {};
u8 grp = 0; /* Send everything on CPU group 0 */
unsigned int i, count, last;
int port = priv->chip_port; int port = priv->chip_port;
u32 rew_op = 0;
val = ocelot_read(ocelot, QS_INJ_STATUS); if (!ocelot_can_inject(ocelot, 0))
if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
(val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp))))
return NETDEV_TX_BUSY; return NETDEV_TX_BUSY;
ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);
info.port = BIT(port);
info.tag_type = IFH_TAG_TYPE_C;
info.vid = skb_vlan_tag_get(skb);
/* Check if timestamping is needed */ /* Check if timestamping is needed */
if (ocelot->ptp && (shinfo->tx_flags & SKBTX_HW_TSTAMP)) { if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
info.rew_op = ocelot_port->ptp_cmd; rew_op = ocelot_port->ptp_cmd;
if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
struct sk_buff *clone; struct sk_buff *clone;
...@@ -547,45 +514,11 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -547,45 +514,11 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
ocelot_port_add_txtstamp_skb(ocelot, port, clone); ocelot_port_add_txtstamp_skb(ocelot, port, clone);
info.rew_op |= clone->cb[0] << 3; rew_op |= clone->cb[0] << 3;
} }
} }
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) { ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
info.rew_op = ocelot_port->ptp_cmd;
if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
info.rew_op |= skb->cb[0] << 3;
}
ocelot_gen_ifh(ifh, &info);
for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
ocelot_write_rix(ocelot, (__force u32)cpu_to_be32(ifh[i]),
QS_INJ_WR, grp);
count = (skb->len + 3) / 4;
last = skb->len % 4;
for (i = 0; i < count; i++)
ocelot_write_rix(ocelot, ((u32 *)skb->data)[i], QS_INJ_WR, grp);
/* Add padding */
while (i < (OCELOT_BUFFER_CELL_SZ / 4)) {
ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
i++;
}
/* Indicate EOF and valid bytes in last word */
ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
QS_INJ_CTRL_VLD_BYTES(skb->len < OCELOT_BUFFER_CELL_SZ ? 0 : last) |
QS_INJ_CTRL_EOF,
QS_INJ_CTRL, grp);
/* Add dummy CRC */
ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
skb_tx_timestamp(skb);
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
kfree_skb(skb); kfree_skb(skb);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* *
* Copyright (c) 2017 Microsemi Corporation * Copyright (c) 2017 Microsemi Corporation
*/ */
#include <linux/dsa/ocelot.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_net.h> #include <linux/of_net.h>
...@@ -18,8 +19,6 @@ ...@@ -18,8 +19,6 @@
#include <soc/mscc/ocelot_hsio.h> #include <soc/mscc/ocelot_hsio.h>
#include "ocelot.h" #include "ocelot.h"
#define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0))
static const u32 ocelot_ana_regmap[] = { static const u32 ocelot_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x009000), REG(ANA_ADVLEARN, 0x009000),
REG(ANA_VLANMASK, 0x009004), REG(ANA_VLANMASK, 0x009004),
...@@ -532,181 +531,28 @@ static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops) ...@@ -532,181 +531,28 @@ static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops)
return 0; return 0;
} }
static int ocelot_parse_ifh(u32 *_ifh, struct frame_info *info)
{
u8 llen, wlen;
u64 ifh[2];
ifh[0] = be64_to_cpu(((__force __be64 *)_ifh)[0]);
ifh[1] = be64_to_cpu(((__force __be64 *)_ifh)[1]);
wlen = IFH_EXTRACT_BITFIELD64(ifh[0], 7, 8);
llen = IFH_EXTRACT_BITFIELD64(ifh[0], 15, 6);
info->len = OCELOT_BUFFER_CELL_SZ * wlen + llen - 80;
info->timestamp = IFH_EXTRACT_BITFIELD64(ifh[0], 21, 32);
info->port = IFH_EXTRACT_BITFIELD64(ifh[1], 43, 4);
info->tag_type = IFH_EXTRACT_BITFIELD64(ifh[1], 16, 1);
info->vid = IFH_EXTRACT_BITFIELD64(ifh[1], 0, 12);
return 0;
}
static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh,
u32 *rval)
{
u32 val;
u32 bytes_valid;
val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
if (val == XTR_NOT_READY) {
if (ifh)
return -EIO;
do {
val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
} while (val == XTR_NOT_READY);
}
switch (val) {
case XTR_ABORT:
return -EIO;
case XTR_EOF_0:
case XTR_EOF_1:
case XTR_EOF_2:
case XTR_EOF_3:
case XTR_PRUNED:
bytes_valid = XTR_VALID_BYTES(val);
val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
if (val == XTR_ESCAPE)
*rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
else
*rval = val;
return bytes_valid;
case XTR_ESCAPE:
*rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
return 4;
default:
*rval = val;
return 4;
}
}
static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg) static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
{ {
struct ocelot *ocelot = arg; struct ocelot *ocelot = arg;
int i = 0, grp = 0; int grp = 0, err;
int err = 0;
if (!(ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)))
return IRQ_NONE;
do { while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) {
struct skb_shared_hwtstamps *shhwtstamps;
struct ocelot_port_private *priv;
struct ocelot_port *ocelot_port;
u64 tod_in_ns, full_ts_in_ns;
struct frame_info info = {};
struct net_device *dev;
u32 ifh[4], val, *buf;
struct timespec64 ts;
int sz, len, buf_len;
struct sk_buff *skb; struct sk_buff *skb;
for (i = 0; i < OCELOT_TAG_LEN / 4; i++) { err = ocelot_xtr_poll_frame(ocelot, grp, &skb);
err = ocelot_rx_frame_word(ocelot, grp, true, &ifh[i]); if (err)
if (err != 4) goto out;
break;
}
if (err != 4)
break;
/* At this point the IFH was read correctly, so it is safe to
* presume that there is no error. The err needs to be reset
* otherwise a frame could come in CPU queue between the while
* condition and the check for error later on. And in that case
* the new frame is just removed and not processed.
*/
err = 0;
ocelot_parse_ifh(ifh, &info);
ocelot_port = ocelot->ports[info.port];
priv = container_of(ocelot_port, struct ocelot_port_private,
port);
dev = priv->dev;
skb = netdev_alloc_skb(dev, info.len);
if (unlikely(!skb)) {
netdev_err(dev, "Unable to allocate sk_buff\n");
err = -ENOMEM;
break;
}
buf_len = info.len - ETH_FCS_LEN;
buf = (u32 *)skb_put(skb, buf_len);
len = 0;
do {
sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
*buf++ = val;
len += sz;
} while (len < buf_len);
/* Read the FCS */
sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
/* Update the statistics if part of the FCS was read before */
len -= ETH_FCS_LEN - sz;
if (unlikely(dev->features & NETIF_F_RXFCS)) {
buf = (u32 *)skb_put(skb, ETH_FCS_LEN);
*buf = val;
}
if (sz < 0) {
err = sz;
break;
}
if (ocelot->ptp) {
ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec);
if ((tod_in_ns & 0xffffffff) < info.timestamp)
full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) |
info.timestamp;
else
full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) |
info.timestamp;
shhwtstamps = skb_hwtstamps(skb);
memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
shhwtstamps->hwtstamp = full_ts_in_ns;
}
/* Everything we see on an interface that is in the HW bridge skb->dev->stats.rx_bytes += skb->len;
* has already been forwarded. skb->dev->stats.rx_packets++;
*/
if (ocelot->bridge_mask & BIT(info.port))
skb->offload_fwd_mark = 1;
skb->protocol = eth_type_trans(skb, dev);
if (!skb_defer_rx_timestamp(skb)) if (!skb_defer_rx_timestamp(skb))
netif_rx(skb); netif_rx(skb);
dev->stats.rx_bytes += len; }
dev->stats.rx_packets++;
} while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp));
if (err) out:
while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) if (err < 0)
ocelot_read_rix(ocelot, QS_XTR_RD, grp); ocelot_drain_cpu_queue(ocelot, 0);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
/* SPDX-License-Identifier: GPL-2.0
* Copyright 2019-2021 NXP Semiconductors
*/
#ifndef _NET_DSA_TAG_OCELOT_H
#define _NET_DSA_TAG_OCELOT_H
#include <linux/packing.h>
#define OCELOT_TAG_LEN 16
#define OCELOT_SHORT_PREFIX_LEN 4
#define OCELOT_LONG_PREFIX_LEN 16
#define OCELOT_TOTAL_TAG_LEN (OCELOT_SHORT_PREFIX_LEN + OCELOT_TAG_LEN)
/* The CPU injection header and the CPU extraction header can have 3 types of
* prefixes: long, short and no prefix. The format of the header itself is the
* same in all 3 cases.
*
* Extraction with long prefix:
*
* +-------------------+-------------------+------+------+------------+-------+
* | ff:ff:ff:ff:ff:ff | fe:ff:ff:ff:ff:ff | 8880 | 000a | extraction | frame |
* | | | | | header | |
* +-------------------+-------------------+------+------+------------+-------+
* 48 bits 48 bits 16 bits 16 bits 128 bits
*
* Extraction with short prefix:
*
* +------+------+------------+-------+
* | 8880 | 000a | extraction | frame |
* | | | header | |
* +------+------+------------+-------+
* 16 bits 16 bits 128 bits
*
* Extraction with no prefix:
*
* +------------+-------+
* | extraction | frame |
* | header | |
* +------------+-------+
* 128 bits
*
*
* Injection with long prefix:
*
* +-------------------+-------------------+------+------+------------+-------+
* | any dmac | any smac | 8880 | 000a | injection | frame |
* | | | | | header | |
* +-------------------+-------------------+------+------+------------+-------+
* 48 bits 48 bits 16 bits 16 bits 128 bits
*
* Injection with short prefix:
*
* +------+------+------------+-------+
* | 8880 | 000a | injection | frame |
* | | | header | |
* +------+------+------------+-------+
* 16 bits 16 bits 128 bits
*
* Injection with no prefix:
*
* +------------+-------+
* | injection | frame |
* | header | |
* +------------+-------+
* 128 bits
*
* The injection header looks like this (network byte order, bit 127
* is part of lowest address byte in memory, bit 0 is part of highest
* address byte):
*
* +------+------+------+------+------+------+------+------+
* 127:120 |BYPASS| MASQ | MASQ_PORT |REW_OP|REW_OP|
* +------+------+------+------+------+------+------+------+
* 119:112 | REW_OP |
* +------+------+------+------+------+------+------+------+
* 111:104 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 103: 96 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 95: 88 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 87: 80 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 79: 72 | RSV |
* +------+------+------+------+------+------+------+------+
* 71: 64 | RSV | DEST |
* +------+------+------+------+------+------+------+------+
* 63: 56 | DEST |
* +------+------+------+------+------+------+------+------+
* 55: 48 | RSV |
* +------+------+------+------+------+------+------+------+
* 47: 40 | RSV | SRC_PORT | RSV |TFRM_TIMER|
* +------+------+------+------+------+------+------+------+
* 39: 32 | TFRM_TIMER | RSV |
* +------+------+------+------+------+------+------+------+
* 31: 24 | RSV | DP | POP_CNT | CPUQ |
* +------+------+------+------+------+------+------+------+
* 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
* +------+------+------+------+------+------+------+------+
* 15: 8 | PCP | DEI | VID |
* +------+------+------+------+------+------+------+------+
* 7: 0 | VID |
* +------+------+------+------+------+------+------+------+
*
* And the extraction header looks like this:
*
* +------+------+------+------+------+------+------+------+
* 127:120 | RSV | REW_OP |
* +------+------+------+------+------+------+------+------+
* 119:112 | REW_OP | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 111:104 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 103: 96 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 95: 88 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 87: 80 | REW_VAL | LLEN |
* +------+------+------+------+------+------+------+------+
* 79: 72 | LLEN | WLEN |
* +------+------+------+------+------+------+------+------+
* 71: 64 | WLEN | RSV |
* +------+------+------+------+------+------+------+------+
* 63: 56 | RSV |
* +------+------+------+------+------+------+------+------+
* 55: 48 | RSV |
* +------+------+------+------+------+------+------+------+
* 47: 40 | RSV | SRC_PORT | ACL_ID |
* +------+------+------+------+------+------+------+------+
* 39: 32 | ACL_ID | RSV | SFLOW_ID |
* +------+------+------+------+------+------+------+------+
* 31: 24 |ACL_HIT| DP | LRN_FLAGS | CPUQ |
* +------+------+------+------+------+------+------+------+
* 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
* +------+------+------+------+------+------+------+------+
* 15: 8 | PCP | DEI | VID |
* +------+------+------+------+------+------+------+------+
* 7: 0 | VID |
* +------+------+------+------+------+------+------+------+
*/
static inline void ocelot_xfh_get_rew_val(void *extraction, u64 *rew_val)
{
packing(extraction, rew_val, 116, 85, OCELOT_TAG_LEN, UNPACK, 0);
}
static inline void ocelot_xfh_get_len(void *extraction, u64 *len)
{
u64 llen, wlen;
packing(extraction, &llen, 84, 79, OCELOT_TAG_LEN, UNPACK, 0);
packing(extraction, &wlen, 78, 71, OCELOT_TAG_LEN, UNPACK, 0);
*len = 60 * wlen + llen - 80;
}
static inline void ocelot_xfh_get_src_port(void *extraction, u64 *src_port)
{
packing(extraction, src_port, 46, 43, OCELOT_TAG_LEN, UNPACK, 0);
}
static inline void ocelot_xfh_get_qos_class(void *extraction, u64 *qos_class)
{
packing(extraction, qos_class, 19, 17, OCELOT_TAG_LEN, UNPACK, 0);
}
static inline void ocelot_xfh_get_tag_type(void *extraction, u64 *tag_type)
{
packing(extraction, tag_type, 16, 16, OCELOT_TAG_LEN, UNPACK, 0);
}
static inline void ocelot_xfh_get_vlan_tci(void *extraction, u64 *vlan_tci)
{
packing(extraction, vlan_tci, 15, 0, OCELOT_TAG_LEN, UNPACK, 0);
}
static inline void ocelot_ifh_set_bypass(void *injection, u64 bypass)
{
packing(injection, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0);
}
static inline void ocelot_ifh_set_rew_op(void *injection, u64 rew_op)
{
packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
}
static inline void ocelot_ifh_set_dest(void *injection, u64 dest)
{
packing(injection, &dest, 67, 56, OCELOT_TAG_LEN, PACK, 0);
}
static inline void ocelot_ifh_set_qos_class(void *injection, u64 qos_class)
{
packing(injection, &qos_class, 19, 17, OCELOT_TAG_LEN, PACK, 0);
}
static inline void seville_ifh_set_dest(void *injection, u64 dest)
{
packing(injection, &dest, 67, 57, OCELOT_TAG_LEN, PACK, 0);
}
static inline void ocelot_ifh_set_src(void *injection, u64 src)
{
packing(injection, &src, 46, 43, OCELOT_TAG_LEN, PACK, 0);
}
static inline void ocelot_ifh_set_tag_type(void *injection, u64 tag_type)
{
packing(injection, &tag_type, 16, 16, OCELOT_TAG_LEN, PACK, 0);
}
static inline void ocelot_ifh_set_vid(void *injection, u64 vid)
{
packing(injection, &vid, 11, 0, OCELOT_TAG_LEN, PACK, 0);
}
#endif
...@@ -48,6 +48,7 @@ struct phylink_link_state; ...@@ -48,6 +48,7 @@ struct phylink_link_state;
#define DSA_TAG_PROTO_HELLCREEK_VALUE 18 #define DSA_TAG_PROTO_HELLCREEK_VALUE 18
#define DSA_TAG_PROTO_XRS700X_VALUE 19 #define DSA_TAG_PROTO_XRS700X_VALUE 19
#define DSA_TAG_PROTO_OCELOT_8021Q_VALUE 20 #define DSA_TAG_PROTO_OCELOT_8021Q_VALUE 20
#define DSA_TAG_PROTO_SEVILLE_VALUE 21
enum dsa_tag_protocol { enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
...@@ -71,6 +72,7 @@ enum dsa_tag_protocol { ...@@ -71,6 +72,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_HELLCREEK = DSA_TAG_PROTO_HELLCREEK_VALUE, DSA_TAG_PROTO_HELLCREEK = DSA_TAG_PROTO_HELLCREEK_VALUE,
DSA_TAG_PROTO_XRS700X = DSA_TAG_PROTO_XRS700X_VALUE, DSA_TAG_PROTO_XRS700X = DSA_TAG_PROTO_XRS700X_VALUE,
DSA_TAG_PROTO_OCELOT_8021Q = DSA_TAG_PROTO_OCELOT_8021Q_VALUE, DSA_TAG_PROTO_OCELOT_8021Q = DSA_TAG_PROTO_OCELOT_8021Q_VALUE,
DSA_TAG_PROTO_SEVILLE = DSA_TAG_PROTO_SEVILLE_VALUE,
}; };
struct packet_type; struct packet_type;
......
...@@ -87,9 +87,6 @@ ...@@ -87,9 +87,6 @@
/* Source PGIDs, one per physical port */ /* Source PGIDs, one per physical port */
#define PGID_SRC 80 #define PGID_SRC 80
#define IFH_INJ_BYPASS BIT(31)
#define IFH_INJ_POP_CNT_DISABLE (3 << 28)
#define IFH_TAG_TYPE_C 0 #define IFH_TAG_TYPE_C 0
#define IFH_TAG_TYPE_S 1 #define IFH_TAG_TYPE_S 1
...@@ -100,10 +97,6 @@ ...@@ -100,10 +97,6 @@
#define IFH_REW_OP_ORIGIN_PTP 0x5 #define IFH_REW_OP_ORIGIN_PTP 0x5
#define OCELOT_NUM_TC 8 #define OCELOT_NUM_TC 8
#define OCELOT_TAG_LEN 16
#define OCELOT_SHORT_PREFIX_LEN 4
#define OCELOT_LONG_PREFIX_LEN 16
#define OCELOT_TOTAL_TAG_LEN (OCELOT_SHORT_PREFIX_LEN + OCELOT_TAG_LEN)
#define OCELOT_SPEED_2500 0 #define OCELOT_SPEED_2500 0
#define OCELOT_SPEED_1000 1 #define OCELOT_SPEED_1000 1
...@@ -742,6 +735,40 @@ u32 __ocelot_target_read_ix(struct ocelot *ocelot, enum ocelot_target target, ...@@ -742,6 +735,40 @@ u32 __ocelot_target_read_ix(struct ocelot *ocelot, enum ocelot_target target,
void __ocelot_target_write_ix(struct ocelot *ocelot, enum ocelot_target target, void __ocelot_target_write_ix(struct ocelot *ocelot, enum ocelot_target target,
u32 val, u32 reg, u32 offset); u32 val, u32 reg, u32 offset);
/* Packet I/O */
#if IS_ENABLED(CONFIG_MSCC_OCELOT_SWITCH_LIB)
bool ocelot_can_inject(struct ocelot *ocelot, int grp);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb);
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **skb);
void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp);
#else
static inline bool ocelot_can_inject(struct ocelot *ocelot, int grp)
{
return false;
}
static inline void ocelot_port_inject_frame(struct ocelot *ocelot, int port,
int grp, u32 rew_op,
struct sk_buff *skb)
{
}
static inline int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp,
struct sk_buff **skb)
{
return -EIO;
}
static inline void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp)
{
}
#endif
/* Hardware initialization */ /* Hardware initialization */
int ocelot_regfields_init(struct ocelot *ocelot, int ocelot_regfields_init(struct ocelot *ocelot,
const struct reg_field *const regfields); const struct reg_field *const regfields);
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 NXP Semiconductors /* Copyright 2019 NXP Semiconductors
*/ */
#include <linux/dsa/ocelot.h>
#include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot.h>
#include <linux/packing.h>
#include "dsa_priv.h" #include "dsa_priv.h"
/* The CPU injection header and the CPU extraction header can have 3 types of static void ocelot_xmit_ptp(struct dsa_port *dp, void *injection,
* prefixes: long, short and no prefix. The format of the header itself is the struct sk_buff *clone)
* same in all 3 cases. {
* struct ocelot *ocelot = dp->ds->priv;
* Extraction with long prefix: struct ocelot_port *ocelot_port;
* u64 rew_op;
* +-------------------+-------------------+------+------+------------+-------+
* | ff:ff:ff:ff:ff:ff | ff:ff:ff:ff:ff:ff | 8880 | 000a | extraction | frame |
* | | | | | header | |
* +-------------------+-------------------+------+------+------------+-------+
* 48 bits 48 bits 16 bits 16 bits 128 bits
*
* Extraction with short prefix:
*
* +------+------+------------+-------+
* | 8880 | 000a | extraction | frame |
* | | | header | |
* +------+------+------------+-------+
* 16 bits 16 bits 128 bits
*
* Extraction with no prefix:
*
* +------------+-------+
* | extraction | frame |
* | header | |
* +------------+-------+
* 128 bits
*
*
* Injection with long prefix:
*
* +-------------------+-------------------+------+------+------------+-------+
* | any dmac | any smac | 8880 | 000a | injection | frame |
* | | | | | header | |
* +-------------------+-------------------+------+------+------------+-------+
* 48 bits 48 bits 16 bits 16 bits 128 bits
*
* Injection with short prefix:
*
* +------+------+------------+-------+
* | 8880 | 000a | injection | frame |
* | | | header | |
* +------+------+------------+-------+
* 16 bits 16 bits 128 bits
*
* Injection with no prefix:
*
* +------------+-------+
* | injection | frame |
* | header | |
* +------------+-------+
* 128 bits
*
* The injection header looks like this (network byte order, bit 127
* is part of lowest address byte in memory, bit 0 is part of highest
* address byte):
*
* +------+------+------+------+------+------+------+------+
* 127:120 |BYPASS| MASQ | MASQ_PORT |REW_OP|REW_OP|
* +------+------+------+------+------+------+------+------+
* 119:112 | REW_OP |
* +------+------+------+------+------+------+------+------+
* 111:104 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 103: 96 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 95: 88 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 87: 80 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 79: 72 | RSV |
* +------+------+------+------+------+------+------+------+
* 71: 64 | RSV | DEST |
* +------+------+------+------+------+------+------+------+
* 63: 56 | DEST |
* +------+------+------+------+------+------+------+------+
* 55: 48 | RSV |
* +------+------+------+------+------+------+------+------+
* 47: 40 | RSV | SRC_PORT | RSV |TFRM_TIMER|
* +------+------+------+------+------+------+------+------+
* 39: 32 | TFRM_TIMER | RSV |
* +------+------+------+------+------+------+------+------+
* 31: 24 | RSV | DP | POP_CNT | CPUQ |
* +------+------+------+------+------+------+------+------+
* 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
* +------+------+------+------+------+------+------+------+
* 15: 8 | PCP | DEI | VID |
* +------+------+------+------+------+------+------+------+
* 7: 0 | VID |
* +------+------+------+------+------+------+------+------+
*
* And the extraction header looks like this:
*
* +------+------+------+------+------+------+------+------+
* 127:120 | RSV | REW_OP |
* +------+------+------+------+------+------+------+------+
* 119:112 | REW_OP | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 111:104 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 103: 96 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 95: 88 | REW_VAL |
* +------+------+------+------+------+------+------+------+
* 87: 80 | REW_VAL | LLEN |
* +------+------+------+------+------+------+------+------+
* 79: 72 | LLEN | WLEN |
* +------+------+------+------+------+------+------+------+
* 71: 64 | WLEN | RSV |
* +------+------+------+------+------+------+------+------+
* 63: 56 | RSV |
* +------+------+------+------+------+------+------+------+
* 55: 48 | RSV |
* +------+------+------+------+------+------+------+------+
* 47: 40 | RSV | SRC_PORT | ACL_ID |
* +------+------+------+------+------+------+------+------+
* 39: 32 | ACL_ID | RSV | SFLOW_ID |
* +------+------+------+------+------+------+------+------+
* 31: 24 |ACL_HIT| DP | LRN_FLAGS | CPUQ |
* +------+------+------+------+------+------+------+------+
* 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
* +------+------+------+------+------+------+------+------+
* 15: 8 | PCP | DEI | VID |
* +------+------+------+------+------+------+------+------+
* 7: 0 | VID |
* +------+------+------+------+------+------+------+------+
*/
static struct sk_buff *ocelot_xmit(struct sk_buff *skb, ocelot_port = ocelot->ports[dp->index];
struct net_device *netdev) rew_op = ocelot_port->ptp_cmd;
/* Retrieve timestamp ID populated inside skb->cb[0] of the
* clone by ocelot_port_add_txtstamp_skb
*/
if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
rew_op |= clone->cb[0] << 3;
ocelot_ifh_set_rew_op(injection, rew_op);
}
static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev,
__be32 ifh_prefix, void **ifh)
{ {
struct dsa_port *dp = dsa_slave_to_port(netdev); struct dsa_port *dp = dsa_slave_to_port(netdev);
struct sk_buff *clone = DSA_SKB_CB(skb)->clone; struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
struct dsa_switch *ds = dp->ds; struct dsa_switch *ds = dp->ds;
struct ocelot *ocelot = ds->priv; void *injection;
struct ocelot_port *ocelot_port; __be32 *prefix;
u8 *prefix, *injection;
u64 qos_class, rew_op;
ocelot_port = ocelot->ports[dp->index];
injection = skb_push(skb, OCELOT_TAG_LEN); injection = skb_push(skb, OCELOT_TAG_LEN);
prefix = skb_push(skb, OCELOT_SHORT_PREFIX_LEN); prefix = skb_push(skb, OCELOT_SHORT_PREFIX_LEN);
memcpy(prefix, ocelot_port->xmit_template, OCELOT_TOTAL_TAG_LEN); *prefix = ifh_prefix;
memset(injection, 0, OCELOT_TAG_LEN);
/* Fix up the fields which are not statically determined ocelot_ifh_set_bypass(injection, 1);
* in the template ocelot_ifh_set_src(injection, ds->num_ports);
*/ ocelot_ifh_set_qos_class(injection, skb->priority);
qos_class = skb->priority;
packing(injection, &qos_class, 19, 17, OCELOT_TAG_LEN, PACK, 0);
/* TX timestamping was requested */ /* TX timestamping was requested */
if (clone) { if (clone)
rew_op = ocelot_port->ptp_cmd; ocelot_xmit_ptp(dp, injection, clone);
/* Retrieve timestamp ID populated inside skb->cb[0] of the
* clone by ocelot_port_add_txtstamp_skb
*/
if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
rew_op |= clone->cb[0] << 3;
packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0); *ifh = injection;
} }
static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct dsa_port *dp = dsa_slave_to_port(netdev);
void *injection;
ocelot_xmit_common(skb, netdev, cpu_to_be32(0x8880000a), &injection);
ocelot_ifh_set_dest(injection, BIT(dp->index));
return skb;
}
static struct sk_buff *seville_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct dsa_port *dp = dsa_slave_to_port(netdev);
void *injection;
ocelot_xmit_common(skb, netdev, cpu_to_be32(0x88800005), &injection);
seville_ifh_set_dest(injection, BIT(dp->index));
return skb; return skb;
} }
...@@ -177,12 +77,10 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, ...@@ -177,12 +77,10 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
struct net_device *netdev, struct net_device *netdev,
struct packet_type *pt) struct packet_type *pt)
{ {
struct dsa_port *cpu_dp = netdev->dsa_ptr;
struct dsa_switch *ds = cpu_dp->ds;
struct ocelot *ocelot = ds->priv;
u64 src_port, qos_class; u64 src_port, qos_class;
u64 vlan_tci, tag_type; u64 vlan_tci, tag_type;
u8 *start = skb->data; u8 *start = skb->data;
struct dsa_port *dp;
u8 *extraction; u8 *extraction;
u16 vlan_tpid; u16 vlan_tpid;
...@@ -210,10 +108,10 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, ...@@ -210,10 +108,10 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
/* Remove from inet csum the extraction header */ /* Remove from inet csum the extraction header */
skb_postpull_rcsum(skb, start, OCELOT_TOTAL_TAG_LEN); skb_postpull_rcsum(skb, start, OCELOT_TOTAL_TAG_LEN);
packing(extraction, &src_port, 46, 43, OCELOT_TAG_LEN, UNPACK, 0); ocelot_xfh_get_src_port(extraction, &src_port);
packing(extraction, &qos_class, 19, 17, OCELOT_TAG_LEN, UNPACK, 0); ocelot_xfh_get_qos_class(extraction, &qos_class);
packing(extraction, &tag_type, 16, 16, OCELOT_TAG_LEN, UNPACK, 0); ocelot_xfh_get_tag_type(extraction, &tag_type);
packing(extraction, &vlan_tci, 15, 0, OCELOT_TAG_LEN, UNPACK, 0); ocelot_xfh_get_vlan_tci(extraction, &vlan_tci);
skb->dev = dsa_master_find_slave(netdev, 0, src_port); skb->dev = dsa_master_find_slave(netdev, 0, src_port);
if (!skb->dev) if (!skb->dev)
...@@ -243,9 +141,10 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, ...@@ -243,9 +141,10 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
* equal to the pvid of the ingress port and should not be used for * equal to the pvid of the ingress port and should not be used for
* processing. * processing.
*/ */
dp = dsa_slave_to_port(skb->dev);
vlan_tpid = tag_type ? ETH_P_8021AD : ETH_P_8021Q; vlan_tpid = tag_type ? ETH_P_8021AD : ETH_P_8021Q;
if (ocelot->ports[src_port]->vlan_aware && if (dsa_port_is_vlan_filtering(dp) &&
eth_hdr(skb)->h_proto == htons(vlan_tpid)) { eth_hdr(skb)->h_proto == htons(vlan_tpid)) {
u16 dummy_vlan_tci; u16 dummy_vlan_tci;
...@@ -267,7 +166,26 @@ static const struct dsa_device_ops ocelot_netdev_ops = { ...@@ -267,7 +166,26 @@ static const struct dsa_device_ops ocelot_netdev_ops = {
.promisc_on_master = true, .promisc_on_master = true,
}; };
MODULE_LICENSE("GPL v2"); DSA_TAG_DRIVER(ocelot_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT); MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT);
module_dsa_tag_driver(ocelot_netdev_ops); static const struct dsa_device_ops seville_netdev_ops = {
.name = "seville",
.proto = DSA_TAG_PROTO_SEVILLE,
.xmit = seville_xmit,
.rcv = ocelot_rcv,
.overhead = OCELOT_TOTAL_TAG_LEN,
.promisc_on_master = true,
};
DSA_TAG_DRIVER(seville_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SEVILLE);
static struct dsa_tag_driver *ocelot_tag_driver_array[] = {
&DSA_TAG_DRIVER_NAME(ocelot_netdev_ops),
&DSA_TAG_DRIVER_NAME(seville_netdev_ops),
};
module_dsa_tag_drivers(ocelot_tag_driver_array);
MODULE_LICENSE("GPL v2");
...@@ -9,8 +9,36 @@ ...@@ -9,8 +9,36 @@
* that on egress * that on egress
*/ */
#include <linux/dsa/8021q.h> #include <linux/dsa/8021q.h>
#include <soc/mscc/ocelot.h>
#include <soc/mscc/ocelot_ptp.h>
#include "dsa_priv.h" #include "dsa_priv.h"
static struct sk_buff *ocelot_xmit_ptp(struct dsa_port *dp,
struct sk_buff *skb,
struct sk_buff *clone)
{
struct ocelot *ocelot = dp->ds->priv;
struct ocelot_port *ocelot_port;
int port = dp->index;
u32 rew_op;
if (!ocelot_can_inject(ocelot, 0))
return NULL;
ocelot_port = ocelot->ports[port];
rew_op = ocelot_port->ptp_cmd;
/* Retrieve timestamp ID populated inside skb->cb[0] of the
* clone by ocelot_port_add_txtstamp_skb
*/
if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
rew_op |= clone->cb[0] << 3;
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
return NULL;
}
static struct sk_buff *ocelot_xmit(struct sk_buff *skb, static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
struct net_device *netdev) struct net_device *netdev)
{ {
...@@ -18,6 +46,11 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb, ...@@ -18,6 +46,11 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
u16 tx_vid = dsa_8021q_tx_vid(dp->ds, dp->index); u16 tx_vid = dsa_8021q_tx_vid(dp->ds, dp->index);
u16 queue_mapping = skb_get_queue_mapping(skb); u16 queue_mapping = skb_get_queue_mapping(skb);
u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); u8 pcp = netdev_txq_to_tc(netdev, queue_mapping);
struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
/* TX timestamping was requested, so inject through MMIO */
if (clone)
return ocelot_xmit_ptp(dp, skb, clone);
return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q, return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q,
((pcp << VLAN_PRIO_SHIFT) | tx_vid)); ((pcp << VLAN_PRIO_SHIFT) | tx_vid));
...@@ -60,6 +93,7 @@ static const struct dsa_device_ops ocelot_8021q_netdev_ops = { ...@@ -60,6 +93,7 @@ static const struct dsa_device_ops ocelot_8021q_netdev_ops = {
.xmit = ocelot_xmit, .xmit = ocelot_xmit,
.rcv = ocelot_rcv, .rcv = ocelot_rcv,
.overhead = VLAN_HLEN, .overhead = VLAN_HLEN,
.promisc_on_master = true,
}; };
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment