brintos

brintos / linux-shallow public Read only

0
0
Text · 9.4 KiB · 7042764 Raw
366 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Microchip Sparx5 Switch driver3 *4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.5 */6 7#include "sparx5_main_regs.h"8#include "sparx5_main.h"9 10#define XTR_EOF_0     ntohl((__force __be32)0x80000000u)11#define XTR_EOF_1     ntohl((__force __be32)0x80000001u)12#define XTR_EOF_2     ntohl((__force __be32)0x80000002u)13#define XTR_EOF_3     ntohl((__force __be32)0x80000003u)14#define XTR_PRUNED    ntohl((__force __be32)0x80000004u)15#define XTR_ABORT     ntohl((__force __be32)0x80000005u)16#define XTR_ESCAPE    ntohl((__force __be32)0x80000006u)17#define XTR_NOT_READY ntohl((__force __be32)0x80000007u)18 19#define XTR_VALID_BYTES(x)      (4 - ((x) & 3))20 21#define INJ_TIMEOUT_NS 5000022 23void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)24{25	/* Start flush */26	spx5_wr(QS_XTR_FLUSH_FLUSH_SET(BIT(grp)), sparx5, QS_XTR_FLUSH);27 28	/* Allow to drain */29	mdelay(1);30 31	/* All Queues normal */32	spx5_wr(0, sparx5, QS_XTR_FLUSH);33}34 35void sparx5_ifh_parse(u32 *ifh, struct frame_info *info)36{37	u8 *xtr_hdr = (u8 *)ifh;38 39	/* FWD is bit 45-72 (28 bits), but we only read the 27 LSB for now */40	u32 fwd =41		((u32)xtr_hdr[27] << 24) |42		((u32)xtr_hdr[28] << 16) |43		((u32)xtr_hdr[29] <<  8) |44		((u32)xtr_hdr[30] <<  0);45	fwd = (fwd >> 5);46	info->src_port = FIELD_GET(GENMASK(7, 1), fwd);47 48	/*49	 * Bit 270-271 are occasionally unexpectedly set by the hardware,50	 * clear bits before extracting timestamp51	 */52	info->timestamp =53		((u64)(xtr_hdr[2] & GENMASK(5, 0)) << 24) |54		((u64)xtr_hdr[3] << 16) |55		((u64)xtr_hdr[4] <<  8) |56		((u64)xtr_hdr[5] <<  0);57}58 59static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)60{61	bool eof_flag = false, pruned_flag = false, abort_flag = false;62	struct net_device *netdev;63	struct sparx5_port *port;64	struct frame_info fi;65	int i, byte_cnt = 0;66	struct sk_buff *skb;67	u32 ifh[IFH_LEN];68	u32 *rxbuf;69 70	/* Get IFH */71	for (i = 0; i < IFH_LEN; i++)72		ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));73 74	/* Decode IFH (what's needed) */75	sparx5_ifh_parse(ifh, &fi);76 77	/* Map to port netdev */78	port = fi.src_port < SPX5_PORTS ?79		sparx5->ports[fi.src_port] : NULL;80	if (!port || !port->ndev) {81		dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);82		sparx5_xtr_flush(sparx5, grp);83		return;84	}85 86	/* Have netdev, get skb */87	netdev = port->ndev;88	skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);89	if (!skb) {90		sparx5_xtr_flush(sparx5, grp);91		dev_err(sparx5->dev, "No skb allocated\n");92		netdev->stats.rx_dropped++;93		return;94	}95	rxbuf = (u32 *)skb->data;96 97	/* Now, pull frame data */98	while (!eof_flag) {99		u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));100		u32 cmp = val;101 102		if (byte_swap)103			cmp = ntohl((__force __be32)val);104 105		switch (cmp) {106		case XTR_NOT_READY:107			break;108		case XTR_ABORT:109			/* No accompanying data */110			abort_flag = true;111			eof_flag = true;112			break;113		case XTR_EOF_0:114		case XTR_EOF_1:115		case XTR_EOF_2:116		case XTR_EOF_3:117			/* This assumes STATUS_WORD_POS == 1, Status118			 * just after last data119			 */120			if (!byte_swap)121				val = ntohl((__force __be32)val);122			byte_cnt -= (4 - XTR_VALID_BYTES(val));123			eof_flag = true;124			break;125		case XTR_PRUNED:126			/* But get the last 4 bytes as well */127			eof_flag = true;128			pruned_flag = true;129			fallthrough;130		case XTR_ESCAPE:131			*rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));132			byte_cnt += 4;133			rxbuf++;134			break;135		default:136			*rxbuf = val;137			byte_cnt += 4;138			rxbuf++;139		}140	}141 142	if (abort_flag || pruned_flag || !eof_flag) {143		netdev_err(netdev, "Discarded frame: abort:%d pruned:%d eof:%d\n",144			   abort_flag, pruned_flag, eof_flag);145		kfree_skb(skb);146		netdev->stats.rx_dropped++;147		return;148	}149 150	/* Everything we see on an interface that is in the HW bridge151	 * has already been forwarded152	 */153	if (test_bit(port->portno, sparx5->bridge_mask))154		skb->offload_fwd_mark = 1;155 156	/* Finish up skb */157	skb_put(skb, byte_cnt - ETH_FCS_LEN);158	eth_skb_pad(skb);159	sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp);160	skb->protocol = eth_type_trans(skb, netdev);161	netdev->stats.rx_bytes += skb->len;162	netdev->stats.rx_packets++;163	netif_rx(skb);164}165 166static int sparx5_inject(struct sparx5 *sparx5,167			 u32 *ifh,168			 struct sk_buff *skb,169			 struct net_device *ndev)170{171	int grp = INJ_QUEUE;172	u32 val, w, count;173	u8 *buf;174 175	val = spx5_rd(sparx5, QS_INJ_STATUS);176	if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp))) {177		pr_err_ratelimited("Injection: Queue not ready: 0x%lx\n",178				   QS_INJ_STATUS_FIFO_RDY_GET(val));179		return -EBUSY;180	}181 182	/* Indicate SOF */183	spx5_wr(QS_INJ_CTRL_SOF_SET(1) |184		QS_INJ_CTRL_GAP_SIZE_SET(1),185		sparx5, QS_INJ_CTRL(grp));186 187	/* Write the IFH to the chip. */188	for (w = 0; w < IFH_LEN; w++)189		spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp));190 191	/* Write words, round up */192	count = DIV_ROUND_UP(skb->len, 4);193	buf = skb->data;194	for (w = 0; w < count; w++, buf += 4) {195		val = get_unaligned((const u32 *)buf);196		spx5_wr(val, sparx5, QS_INJ_WR(grp));197	}198 199	/* Add padding */200	while (w < (60 / 4)) {201		spx5_wr(0, sparx5, QS_INJ_WR(grp));202		w++;203	}204 205	/* Indicate EOF and valid bytes in last word */206	spx5_wr(QS_INJ_CTRL_GAP_SIZE_SET(1) |207		QS_INJ_CTRL_VLD_BYTES_SET(skb->len < 60 ? 0 : skb->len % 4) |208		QS_INJ_CTRL_EOF_SET(1),209		sparx5, QS_INJ_CTRL(grp));210 211	/* Add dummy CRC */212	spx5_wr(0, sparx5, QS_INJ_WR(grp));213	w++;214 215	val = spx5_rd(sparx5, QS_INJ_STATUS);216	if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {217		struct sparx5_port *port = netdev_priv(ndev);218 219		pr_err_ratelimited("Injection: Watermark reached: 0x%lx\n",220				   QS_INJ_STATUS_WMARK_REACHED_GET(val));221		netif_stop_queue(ndev);222		hrtimer_start(&port->inj_timer, INJ_TIMEOUT_NS,223			      HRTIMER_MODE_REL);224	}225 226	return NETDEV_TX_OK;227}228 229netdev_tx_t sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev)230{231	struct net_device_stats *stats = &dev->stats;232	struct sparx5_port *port = netdev_priv(dev);233	struct sparx5 *sparx5 = port->sparx5;234	u32 ifh[IFH_LEN];235	netdev_tx_t ret;236 237	memset(ifh, 0, IFH_LEN * 4);238	sparx5_set_port_ifh(ifh, port->portno);239 240	if (sparx5->ptp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {241		if (sparx5_ptp_txtstamp_request(port, skb) < 0)242			return NETDEV_TX_BUSY;243 244		sparx5_set_port_ifh_rew_op(ifh, SPARX5_SKB_CB(skb)->rew_op);245		sparx5_set_port_ifh_pdu_type(ifh, SPARX5_SKB_CB(skb)->pdu_type);246		sparx5_set_port_ifh_pdu_w16_offset(ifh, SPARX5_SKB_CB(skb)->pdu_w16_offset);247		sparx5_set_port_ifh_timestamp(ifh, SPARX5_SKB_CB(skb)->ts_id);248	}249 250	skb_tx_timestamp(skb);251	spin_lock(&sparx5->tx_lock);252	if (sparx5->fdma_irq > 0)253		ret = sparx5_fdma_xmit(sparx5, ifh, skb);254	else255		ret = sparx5_inject(sparx5, ifh, skb, dev);256	spin_unlock(&sparx5->tx_lock);257 258	if (ret == -EBUSY)259		goto busy;260	if (ret < 0)261		goto drop;262 263	stats->tx_bytes += skb->len;264	stats->tx_packets++;265	sparx5->tx.packets++;266 267	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&268	    SPARX5_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP)269		return NETDEV_TX_OK;270 271	dev_consume_skb_any(skb);272	return NETDEV_TX_OK;273drop:274	stats->tx_dropped++;275	sparx5->tx.dropped++;276	dev_kfree_skb_any(skb);277	return NETDEV_TX_OK;278busy:279	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&280	    SPARX5_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP)281		sparx5_ptp_txtstamp_release(port, skb);282	return NETDEV_TX_BUSY;283}284 285static enum hrtimer_restart sparx5_injection_timeout(struct hrtimer *tmr)286{287	struct sparx5_port *port = container_of(tmr, struct sparx5_port,288						inj_timer);289	int grp = INJ_QUEUE;290	u32 val;291 292	val = spx5_rd(port->sparx5, QS_INJ_STATUS);293	if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {294		pr_err_ratelimited("Injection: Reset watermark count\n");295		/* Reset Watermark count to restart */296		spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),297			 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,298			 port->sparx5,299			 DSM_DEV_TX_STOP_WM_CFG(port->portno));300	}301	netif_wake_queue(port->ndev);302	return HRTIMER_NORESTART;303}304 305int sparx5_manual_injection_mode(struct sparx5 *sparx5)306{307	const int byte_swap = 1;308	int portno;309 310	/* Change mode to manual extraction and injection */311	spx5_wr(QS_XTR_GRP_CFG_MODE_SET(1) |312		QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(1) |313		QS_XTR_GRP_CFG_BYTE_SWAP_SET(byte_swap),314		sparx5, QS_XTR_GRP_CFG(XTR_QUEUE));315	spx5_wr(QS_INJ_GRP_CFG_MODE_SET(1) |316		QS_INJ_GRP_CFG_BYTE_SWAP_SET(byte_swap),317		sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));318 319	/* CPU ports capture setup */320	for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) {321		/* ASM CPU port: No preamble, IFH, enable padding */322		spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |323			ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |324			ASM_PORT_CFG_INJ_FORMAT_CFG_SET(1), /* 1 = IFH */325			sparx5, ASM_PORT_CFG(portno));326 327		/* Reset WM cnt to unclog queued frames */328		spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),329			 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,330			 sparx5,331			 DSM_DEV_TX_STOP_WM_CFG(portno));332 333		/* Set Disassembler Stop Watermark level */334		spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(0),335			 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,336			 sparx5,337			 DSM_DEV_TX_STOP_WM_CFG(portno));338 339		/* Enable Disassembler buffer underrun watchdog340		 */341		spx5_rmw(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS_SET(0),342			 DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS,343			 sparx5,344			 DSM_BUF_CFG(portno));345	}346	return 0;347}348 349irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)350{351	struct sparx5 *s5 = _sparx5;352	int poll = 64;353 354	/* Check data in queue */355	while (spx5_rd(s5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE) && poll-- > 0)356		sparx5_xtr_grp(s5, XTR_QUEUE, false);357 358	return IRQ_HANDLED;359}360 361void sparx5_port_inj_timer_setup(struct sparx5_port *port)362{363	hrtimer_init(&port->inj_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);364	port->inj_timer.function = sparx5_injection_timeout;365}366