diff options
Diffstat (limited to 'drivers/net/ethernet/microchip/sparx5/sparx5_packet.c')
| -rw-r--r-- | drivers/net/ethernet/microchip/sparx5/sparx5_packet.c | 107 |
1 files changed, 84 insertions, 23 deletions
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c index dc7e5ea6ec15..f713656f1fae 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c @@ -32,7 +32,7 @@ void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp) spx5_wr(0, sparx5, QS_XTR_FLUSH); } -void sparx5_ifh_parse(u32 *ifh, struct frame_info *info) +void sparx5_ifh_parse(struct sparx5 *sparx5, u32 *ifh, struct frame_info *info) { u8 *xtr_hdr = (u8 *)ifh; @@ -43,7 +43,18 @@ void sparx5_ifh_parse(u32 *ifh, struct frame_info *info) ((u32)xtr_hdr[29] << 8) | ((u32)xtr_hdr[30] << 0); fwd = (fwd >> 5); - info->src_port = FIELD_GET(GENMASK(7, 1), fwd); + info->src_port = spx5_field_get(GENMASK(is_sparx5(sparx5) ? 7 : 6, 1), + fwd); + + /* + * Bit 270-271 are occasionally unexpectedly set by the hardware, + * clear bits before extracting timestamp + */ + info->timestamp = + ((u64)(xtr_hdr[2] & GENMASK(5, 0)) << 24) | + ((u64)xtr_hdr[3] << 16) | + ((u64)xtr_hdr[4] << 8) | + ((u64)xtr_hdr[5] << 0); } static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap) @@ -61,11 +72,11 @@ static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap) for (i = 0; i < IFH_LEN; i++) ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp)); - /* Decode IFH (whats needed) */ - sparx5_ifh_parse(ifh, &fi); + /* Decode IFH (what's needed) */ + sparx5_ifh_parse(sparx5, ifh, &fi); /* Map to port netdev */ - port = fi.src_port < SPX5_PORTS ? + port = fi.src_port < sparx5->data->consts->n_ports ? sparx5->ports[fi.src_port] : NULL; if (!port || !port->ndev) { dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port); @@ -107,6 +118,8 @@ static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap) /* This assumes STATUS_WORD_POS == 1, Status * just after last data */ + if (!byte_swap) + val = ntohl((__force __be32)val); byte_cnt -= (4 - XTR_VALID_BYTES(val)); eof_flag = true; break; @@ -144,10 +157,11 @@ static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap) /* Finish up skb */ skb_put(skb, byte_cnt - ETH_FCS_LEN); eth_skb_pad(skb); + sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp); skb->protocol = eth_type_trans(skb, netdev); - netif_rx(skb); netdev->stats.rx_bytes += skb->len; netdev->stats.rx_packets++; + netif_rx(skb); } static int sparx5_inject(struct sparx5 *sparx5, @@ -213,27 +227,72 @@ static int sparx5_inject(struct sparx5 *sparx5, return NETDEV_TX_OK; } -int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = &dev->stats; struct sparx5_port *port = netdev_priv(dev); struct sparx5 *sparx5 = port->sparx5; - int ret; + const struct sparx5_ops *ops; + u32 ifh[IFH_LEN]; + netdev_tx_t ret; + + ops = sparx5->data->ops; + + memset(ifh, 0, IFH_LEN * 4); + sparx5_set_port_ifh(sparx5, ifh, port->portno); + + if (sparx5->ptp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + if (sparx5_ptp_txtstamp_request(port, skb) < 0) + return NETDEV_TX_BUSY; + + sparx5_set_port_ifh_rew_op(ifh, SPARX5_SKB_CB(skb)->rew_op); + sparx5_set_port_ifh_pdu_type(sparx5, ifh, + SPARX5_SKB_CB(skb)->pdu_type); + sparx5_set_port_ifh_pdu_w16_offset(sparx5, ifh, + SPARX5_SKB_CB(skb)->pdu_w16_offset); + sparx5_set_port_ifh_timestamp(sparx5, ifh, + SPARX5_SKB_CB(skb)->ts_id); + } + skb_tx_timestamp(skb); + spin_lock(&sparx5->tx_lock); if (sparx5->fdma_irq > 0) - ret = sparx5_fdma_xmit(sparx5, port->ifh, skb); + ret = ops->fdma_xmit(sparx5, ifh, skb, dev); else - ret = sparx5_inject(sparx5, port->ifh, skb, dev); - - if (ret == NETDEV_TX_OK) { - stats->tx_bytes += skb->len; - stats->tx_packets++; - skb_tx_timestamp(skb); - dev_kfree_skb_any(skb); - } else { - stats->tx_dropped++; - } - return ret; + ret = sparx5_inject(sparx5, ifh, skb, dev); + spin_unlock(&sparx5->tx_lock); + + if (ret == -EBUSY) + goto busy; + if (ret < 0) + goto drop; + + if (!is_sparx5(sparx5)) + /* When lan969x and TX_OK, stats and SKB consumption is handled + * in the TX completion loop, so dont go any further. + */ + return NETDEV_TX_OK; + + stats->tx_bytes += skb->len; + stats->tx_packets++; + sparx5->tx.packets++; + + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + SPARX5_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) + return NETDEV_TX_OK; + + dev_consume_skb_any(skb); + return NETDEV_TX_OK; +drop: + stats->tx_dropped++; + sparx5->tx.dropped++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +busy: + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + SPARX5_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) + sparx5_ptp_txtstamp_release(port, skb); + return NETDEV_TX_BUSY; } static enum hrtimer_restart sparx5_injection_timeout(struct hrtimer *tmr) @@ -271,7 +330,9 @@ int sparx5_manual_injection_mode(struct sparx5 *sparx5) sparx5, QS_INJ_GRP_CFG(INJ_QUEUE)); /* CPU ports capture setup */ - for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) { + for (portno = sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_0); + portno <= sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_1); + portno++) { /* ASM CPU port: No preamble, IFH, enable padding */ spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) | ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) | @@ -314,6 +375,6 @@ irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5) void sparx5_port_inj_timer_setup(struct sparx5_port *port) { - hrtimer_init(&port->inj_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - port->inj_timer.function = sparx5_injection_timeout; + hrtimer_setup(&port->inj_timer, sparx5_injection_timeout, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); } |
