@@ -733,6 +733,18 @@ static bool felix_rxtstamp(struct dsa_switch *ds, int port,
struct timespec64 ts;
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 the "no XTR IRQ" workaround isn't in use, this is a no-op and
+ * should be eliminated by the compiler as dead code.
+ */
+ if (felix_check_xtr_pkt(ocelot, type)) {
+ kfree_skb(skb);
+ return true;
+ }
+
ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
tstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
@@ -11,9 +11,70 @@
#include <soc/mscc/ocelot_vcap.h>
#include <linux/dsa/8021q.h>
#include <linux/if_bridge.h>
+#include <linux/ptp_classify.h>
#include "felix.h"
#include "felix_tag_8021q.h"
+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->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 ocelot_frame_info info = {};
+ struct dsa_port *dp;
+ struct sk_buff *skb;
+ unsigned int type;
+
+ err = ocelot_xtr_poll_xfh(ocelot, grp, &info);
+ if (err)
+ break;
+
+ if (WARN_ON(info.port >= ocelot->num_phys_ports))
+ goto out;
+
+ dp = dsa_to_port(felix->ds, info.port);
+
+ err = ocelot_xtr_poll_frame(ocelot, grp, dp->slave,
+ &info, &skb);
+ if (err)
+ break;
+
+ /* 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_write(ocelot, QS_XTR_FLUSH, BIT(grp));
+ ocelot_write(ocelot, QS_XTR_FLUSH, 0);
+ }
+
+ return true;
+}
+
static int felix_tag_8021q_rxvlan_add(struct felix *felix, int port, u16 vid,
bool pvid, bool untagged)
{
@@ -7,6 +7,7 @@
#if IS_ENABLED(CONFIG_NET_DSA_TAG_OCELOT_8021Q)
int felix_setup_8021q_tagging(struct ocelot *ocelot);
+bool felix_check_xtr_pkt(struct ocelot *ocelot, unsigned int ptp_type);
#else
@@ -15,6 +16,12 @@ static inline int felix_setup_8021q_tagging(struct ocelot *ocelot)
return -EOPNOTSUPP;
}
+static inline bool felix_check_xtr_pkt(struct ocelot *ocelot,
+ unsigned int ptp_type)
+{
+ return false;
+}
+
#endif /* IS_ENABLED(CONFIG_NET_DSA_TAG_OCELOT_8021Q) */
#endif /* _MSCC_FELIX_TAG_8021Q_H */
@@ -646,6 +646,7 @@ int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp,
return 0;
}
+EXPORT_SYMBOL(ocelot_xtr_poll_xfh);
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp,
struct net_device *dev,
@@ -723,6 +724,7 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp,
out:
return err;
}
+EXPORT_SYMBOL(ocelot_xtr_poll_frame);
/* Generate the IFH for frame injection
*
@@ -801,6 +803,7 @@ void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
skb->dev->stats.tx_packets++;
skb->dev->stats.tx_bytes += skb->len;
}
+EXPORT_SYMBOL(ocelot_port_inject_frame);
int ocelot_fdb_add(struct ocelot *ocelot, int port,
const unsigned char *addr, u16 vid)
@@ -119,14 +119,6 @@ int ocelot_port_devlink_init(struct ocelot *ocelot, int port,
void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port);
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_xfh(struct ocelot *ocelot, int grp,
- struct ocelot_frame_info *info);
-int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp,
- struct net_device *dev,
- struct ocelot_frame_info *info,
- struct sk_buff **skb);
extern struct notifier_block ocelot_netdevice_nb;
extern struct notifier_block ocelot_switchdev_nb;
@@ -840,4 +840,13 @@ int ocelot_sb_occ_tc_port_bind_get(struct ocelot *ocelot, int port,
enum devlink_sb_pool_type pool_type,
u32 *p_cur, u32 *p_max);
+void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
+ u32 rew_op, struct sk_buff *skb);
+int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp,
+ struct ocelot_frame_info *info);
+int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp,
+ struct net_device *dev,
+ struct ocelot_frame_info *info,
+ struct sk_buff **skb);
+
#endif
@@ -2,6 +2,8 @@
/* Copyright 2020-2021 NXP Semiconductors
*/
#include <linux/dsa/8021q.h>
+#include <soc/mscc/ocelot.h>
+#include <soc/mscc/ocelot_ptp.h>
#include "dsa_priv.h"
static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
@@ -11,6 +13,28 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
u16 tx_vid = dsa_8021q_tx_vid(dp->ds, dp->index);
u16 queue_mapping = skb_get_queue_mapping(skb);
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) {
+ struct ocelot *ocelot = dp->ds->priv;
+ struct ocelot_port *ocelot_port;
+ int port = dp->index;
+ u32 rew_op;
+
+ 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, dp->index, 0, rew_op, skb);
+
+ return NULL;
+ }
return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q,
((pcp << VLAN_PRIO_SHIFT) | tx_vid));