diff --git a/drivers/net/ethernet/ti/prueth.h b/drivers/net/ethernet/ti/prueth.h index 3259c84f6f795f8234ed3a8c45d7278abeee6edc..4af2c4cfd3c4c87a87d2b7c6f7a98f000a947842 100644 --- a/drivers/net/ethernet/ti/prueth.h +++ b/drivers/net/ethernet/ti/prueth.h @@ -382,6 +382,7 @@ struct prueth_emac { struct sk_buff *ptp_skb[PRUETH_PTP_TS_EVENTS]; spinlock_t ptp_skb_lock; /* serialize access */ int emac_ptp_tx_irq; + int hsr_ptp_tx_irq; bool ptp_tx_enable; }; @@ -488,6 +489,8 @@ int emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, const struct prueth_queue_info *rxqueue); int emac_add_del_vid(struct prueth_emac *emac, bool add, __be16 proto, u16 vid); +irqreturn_t prueth_ptp_tx_irq_handle(int irq, void *dev); +irqreturn_t prueth_ptp_tx_irq_work(int irq, void *dev); extern const struct prueth_queue_desc queue_descs[][NUM_QUEUES]; diff --git a/drivers/net/ethernet/ti/prueth_core.c b/drivers/net/ethernet/ti/prueth_core.c index be4d0d23593ef6909ac98d481bd0892da88052dc..51091ea85fc961c16d24beee578b7f3711fc0861 100644 --- a/drivers/net/ethernet/ti/prueth_core.c +++ b/drivers/net/ethernet/ti/prueth_core.c @@ -741,13 +741,26 @@ static irqreturn_t emac_rx_hardirq(int irq, void *dev_id) static u8 prueth_ptp_ts_event_type(struct sk_buff *skb) { - unsigned int ptp_class = ptp_classify_raw(skb); - u8 *msgtype, *data = skb->data, event_type; - unsigned int offset = 0; + u8 *msgtype, *data, event_type, changed = 0; + unsigned int offset = 0, ptp_class; u16 *seqid; - if (ptp_class == PTP_CLASS_NONE) - return PRUETH_PTP_TS_EVENTS; + if (eth_hdr(skb)->h_proto == htons(ETH_P_HSR)) { + /* This 6-byte shift is just a trick to skip + * the size of a hsr tag so that the same + * pruptp_ts_msgtype can be re-used to parse + * hsr tagged skbs + */ + skb->data += 6; + changed = 6; + } + + ptp_class = ptp_classify_raw(skb); + data = skb->data; + if (ptp_class == PTP_CLASS_NONE) { + event_type = PRUETH_PTP_TS_EVENTS; + goto exit; + } if (ptp_class & PTP_CLASS_VLAN) offset += VLAN_HLEN; @@ -766,8 +779,10 @@ static u8 prueth_ptp_ts_event_type(struct sk_buff *skb) return PRUETH_PTP_TS_EVENTS; } - if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid)) - return PRUETH_PTP_TS_EVENTS; + if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid)) { + event_type = PRUETH_PTP_TS_EVENTS; + goto exit; + } if (unlikely(ptp_class & PTP_CLASS_V1)) msgtype = data + offset + OFF_PTP_CONTROL; @@ -796,6 +811,9 @@ static u8 prueth_ptp_ts_event_type(struct sk_buff *skb) event_type = PRUETH_PTP_TS_EVENTS; } +exit: + skb->data -= changed; + return event_type; } @@ -836,7 +854,7 @@ static int prueth_ptp_tx_ts_enqueue(struct prueth_emac *emac, struct sk_buff *sk return 0; } -static irqreturn_t prueth_ptp_tx_irq_handle(int irq, void *dev) +irqreturn_t prueth_ptp_tx_irq_handle(int irq, void *dev) { struct net_device *ndev = (struct net_device *)dev; struct prueth_emac *emac = netdev_priv(ndev); @@ -874,6 +892,15 @@ static void prueth_ptp_tx_ts_get(struct prueth_emac *emac, u8 event) emac->ptp_skb[event] = NULL; spin_unlock_irqrestore(&emac->ptp_skb_lock, flags); if (!skb) { + /* In case of HSR, tx timestamp may be generated by + * cut-through packets such as SYNC, which does not + * have a corresponding queued tx packet. Such a tx + * timestamp is consumed by the rx port when processing + * the rx timestamp. + */ + if (PRUETH_IS_LRE(emac->prueth)) + return; + netdev_err(emac->ndev, "no tx msg %u found waiting for ts\n", event); return; @@ -889,7 +916,7 @@ static void prueth_ptp_tx_ts_get(struct prueth_emac *emac, u8 event) dev_consume_skb_any(skb); } -static irqreturn_t prueth_ptp_tx_irq_work(int irq, void *dev) +irqreturn_t prueth_ptp_tx_irq_work(int irq, void *dev) { struct prueth_emac *emac = netdev_priv(dev); u32 ts_notify_offs, ts_notify_mask, i; @@ -1202,6 +1229,7 @@ int emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, ptr = nt_dst_addr + PRUETH_ETH_TYPE_OFFSET; type = (*ptr++) << PRUETH_ETH_TYPE_UPPER_SHIFT; type |= *ptr++; + eth_hdr(skb)->h_proto = htons(type); if (type == ETH_P_8021Q) offset = 4; } @@ -2641,6 +2669,12 @@ static int prueth_netdev_init(struct prueth *prueth, dev_err(prueth->dev, "could not get ptp tx irq. Skipping PTP support\n"); } + emac->hsr_ptp_tx_irq = of_irq_get_byname(eth_node, "hsr_ptp_tx"); + if (emac->hsr_ptp_tx_irq < 0) { + emac->hsr_ptp_tx_irq = 0; + dev_err(prueth->dev, "could not get hsr ptp tx irq. Skipping PTP support\n"); + } + emac->msg_enable = netif_msg_init(debug_level, PRUETH_EMAC_DEBUG); spin_lock_init(&emac->lock); spin_lock_init(&emac->nsp_lock); diff --git a/drivers/net/ethernet/ti/prueth_lre.c b/drivers/net/ethernet/ti/prueth_lre.c index 15ab6b9e21416563f7cecc96e6b19d6ad615b9fc..275138c09a93f995e515257994ac6cc711def654 100644 --- a/drivers/net/ethernet/ti/prueth_lre.c +++ b/drivers/net/ethernet/ti/prueth_lre.c @@ -858,6 +858,19 @@ int prueth_lre_request_irqs(struct prueth_emac *emac) struct prueth *prueth = emac->prueth; int ret; + if (emac->hsr_ptp_tx_irq) { + ret = request_threaded_irq(emac->hsr_ptp_tx_irq, + prueth_ptp_tx_irq_handle, + prueth_ptp_tx_irq_work, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + emac->ndev->name, emac->ndev); + if (ret) { + netdev_err(emac->ndev, "unable to request PTP TX IRQ\n"); + return ret; + } + + } + /* HSR/PRP. Request irq when first port is initialized */ if (prueth->emac_configured) return 0; @@ -866,17 +879,24 @@ int prueth_lre_request_irqs(struct prueth_emac *emac) IRQF_TRIGGER_HIGH, "eth_hp_int", prueth->hp); if (ret) { netdev_err(emac->ndev, "unable to request RX HPQ IRQ\n"); - return ret; + goto free_ptp_irq; } ret = request_irq(prueth->rx_lpq_irq, prueth_lre_emac_rx_hardirq, IRQF_TRIGGER_HIGH, "eth_lp_int", prueth->lp); if (ret) { netdev_err(emac->ndev, "unable to request RX LPQ IRQ\n"); - free_irq(prueth->rx_hpq_irq, prueth->hp); - return ret; + goto free_rx_hpq_irq; } + return 0; + +free_rx_hpq_irq: + free_irq(prueth->rx_hpq_irq, prueth->hp); +free_ptp_irq: + if (emac->hsr_ptp_tx_irq) + free_irq(emac->hsr_ptp_tx_irq, emac->ndev); + return ret; }