From f3097be21bf17ae8785eea009cbc424f16611d9a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 8 Jun 2019 15:04:42 +0300 Subject: net: dsa: sja1105: Add a state machine for RX timestamping Meta frame reception relies on the hardware keeping its promise that it will send no other traffic towards the CPU port between a link-local frame and a meta frame. Otherwise there is no other way to associate the meta frame with the link-local frame it's holding a timestamp of. The receive function is made stateful, and buffers a timestampable frame until its meta frame arrives, then merges the two, drops the meta and releases the link-local frame up the stack. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_main.c | 62 ++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'drivers/net/dsa/sja1105') diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 2b804eeca390..8963b21b3061 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1600,6 +1600,14 @@ static int sja1105_setup(struct dsa_switch *ds) return sja1105_setup_8021q_tagging(ds, true); } +static void sja1105_teardown(struct dsa_switch *ds) +{ + struct sja1105_private *priv = ds->priv; + + cancel_work_sync(&priv->tagger_data.rxtstamp_work); + skb_queue_purge(&priv->tagger_data.skb_rxtstamp_queue); +} + static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, struct sk_buff *skb, bool takets) { @@ -1747,6 +1755,57 @@ static int sja1105_set_ageing_time(struct dsa_switch *ds, return sja1105_static_config_reload(priv); } +#define to_tagger(d) \ + container_of((d), struct sja1105_tagger_data, rxtstamp_work) +#define to_sja1105(d) \ + container_of((d), struct sja1105_private, tagger_data) + +static void sja1105_rxtstamp_work(struct work_struct *work) +{ + struct sja1105_tagger_data *data = to_tagger(work); + struct sja1105_private *priv = to_sja1105(data); + struct sk_buff *skb; + u64 now; + + mutex_lock(&priv->ptp_lock); + + now = priv->tstamp_cc.read(&priv->tstamp_cc); + + while ((skb = skb_dequeue(&data->skb_rxtstamp_queue)) != NULL) { + struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb); + u64 ts; + + *shwt = (struct skb_shared_hwtstamps) {0}; + + ts = SJA1105_SKB_CB(skb)->meta_tstamp; + ts = sja1105_tstamp_reconstruct(priv, now, ts); + ts = timecounter_cyc2time(&priv->tstamp_tc, ts); + + shwt->hwtstamp = ns_to_ktime(ts); + netif_rx_ni(skb); + } + + mutex_unlock(&priv->ptp_lock); +} + +/* Called from dsa_skb_defer_rx_timestamp */ +bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb, unsigned int type) +{ + struct sja1105_private *priv = ds->priv; + struct sja1105_tagger_data *data = &priv->tagger_data; + + if (!data->hwts_rx_en) + return false; + + /* We need to read the full PTP clock to reconstruct the Rx + * timestamp. For that we need a sleepable context. + */ + skb_queue_tail(&data->skb_rxtstamp_queue, skb); + schedule_work(&data->rxtstamp_work); + return true; +} + /* Called from dsa_skb_tx_timestamp. This callback is just to make DSA clone * the skb and have it available in DSA_SKB_CB in the .port_deferred_xmit * callback, where we will timestamp it synchronously. @@ -1766,6 +1825,7 @@ bool sja1105_port_txtstamp(struct dsa_switch *ds, int port, static const struct dsa_switch_ops sja1105_switch_ops = { .get_tag_protocol = sja1105_get_tag_protocol, .setup = sja1105_setup, + .teardown = sja1105_teardown, .set_ageing_time = sja1105_set_ageing_time, .phylink_validate = sja1105_phylink_validate, .phylink_mac_config = sja1105_mac_config, @@ -1787,6 +1847,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .port_mdb_add = sja1105_mdb_add, .port_mdb_del = sja1105_mdb_del, .port_deferred_xmit = sja1105_port_deferred_xmit, + .port_rxtstamp = sja1105_port_rxtstamp, .port_txtstamp = sja1105_port_txtstamp, }; @@ -1885,6 +1946,7 @@ static int sja1105_probe(struct spi_device *spi) tagger_data = &priv->tagger_data; skb_queue_head_init(&tagger_data->skb_rxtstamp_queue); + INIT_WORK(&tagger_data->rxtstamp_work, sja1105_rxtstamp_work); /* Connections between dsa_port and sja1105_port */ for (i = 0; i < SJA1105_NUM_PORTS; i++) { -- cgit v1.2.3-59-g8ed1b