xref: /openbmc/linux/drivers/net/dsa/bcm_sf2_cfp.c (revision 33061458)
17318166cSFlorian Fainelli /*
27318166cSFlorian Fainelli  * Broadcom Starfighter 2 DSA switch CFP support
37318166cSFlorian Fainelli  *
47318166cSFlorian Fainelli  * Copyright (C) 2016, Broadcom
57318166cSFlorian Fainelli  *
67318166cSFlorian Fainelli  * This program is free software; you can redistribute it and/or modify
77318166cSFlorian Fainelli  * it under the terms of the GNU General Public License as published by
87318166cSFlorian Fainelli  * the Free Software Foundation; either version 2 of the License, or
97318166cSFlorian Fainelli  * (at your option) any later version.
107318166cSFlorian Fainelli  */
117318166cSFlorian Fainelli 
127318166cSFlorian Fainelli #include <linux/list.h>
137318166cSFlorian Fainelli #include <linux/ethtool.h>
147318166cSFlorian Fainelli #include <linux/if_ether.h>
157318166cSFlorian Fainelli #include <linux/in.h>
16c6e970a0SAndrew Lunn #include <linux/netdevice.h>
17c6e970a0SAndrew Lunn #include <net/dsa.h>
187318166cSFlorian Fainelli #include <linux/bitmap.h>
197318166cSFlorian Fainelli 
207318166cSFlorian Fainelli #include "bcm_sf2.h"
217318166cSFlorian Fainelli #include "bcm_sf2_regs.h"
227318166cSFlorian Fainelli 
237318166cSFlorian Fainelli struct cfp_udf_layout {
247318166cSFlorian Fainelli 	u8 slices[UDF_NUM_SLICES];
257318166cSFlorian Fainelli 	u32 mask_value;
267318166cSFlorian Fainelli 
277318166cSFlorian Fainelli };
287318166cSFlorian Fainelli 
297318166cSFlorian Fainelli /* UDF slices layout for a TCPv4/UDPv4 specification */
307318166cSFlorian Fainelli static const struct cfp_udf_layout udf_tcpip4_layout = {
317318166cSFlorian Fainelli 	.slices = {
327318166cSFlorian Fainelli 		/* End of L2, byte offset 12, src IP[0:15] */
337318166cSFlorian Fainelli 		CFG_UDF_EOL2 | 6,
347318166cSFlorian Fainelli 		/* End of L2, byte offset 14, src IP[16:31] */
357318166cSFlorian Fainelli 		CFG_UDF_EOL2 | 7,
367318166cSFlorian Fainelli 		/* End of L2, byte offset 16, dst IP[0:15] */
377318166cSFlorian Fainelli 		CFG_UDF_EOL2 | 8,
387318166cSFlorian Fainelli 		/* End of L2, byte offset 18, dst IP[16:31] */
397318166cSFlorian Fainelli 		CFG_UDF_EOL2 | 9,
407318166cSFlorian Fainelli 		/* End of L3, byte offset 0, src port */
417318166cSFlorian Fainelli 		CFG_UDF_EOL3 | 0,
427318166cSFlorian Fainelli 		/* End of L3, byte offset 2, dst port */
437318166cSFlorian Fainelli 		CFG_UDF_EOL3 | 1,
447318166cSFlorian Fainelli 		0, 0, 0
457318166cSFlorian Fainelli 	},
467318166cSFlorian Fainelli 	.mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG,
477318166cSFlorian Fainelli };
487318166cSFlorian Fainelli 
497318166cSFlorian Fainelli static inline unsigned int bcm_sf2_get_num_udf_slices(const u8 *layout)
507318166cSFlorian Fainelli {
517318166cSFlorian Fainelli 	unsigned int i, count = 0;
527318166cSFlorian Fainelli 
537318166cSFlorian Fainelli 	for (i = 0; i < UDF_NUM_SLICES; i++) {
547318166cSFlorian Fainelli 		if (layout[i] != 0)
557318166cSFlorian Fainelli 			count++;
567318166cSFlorian Fainelli 	}
577318166cSFlorian Fainelli 
587318166cSFlorian Fainelli 	return count;
597318166cSFlorian Fainelli }
607318166cSFlorian Fainelli 
617318166cSFlorian Fainelli static void bcm_sf2_cfp_udf_set(struct bcm_sf2_priv *priv,
627318166cSFlorian Fainelli 				unsigned int slice_num,
637318166cSFlorian Fainelli 				const u8 *layout)
647318166cSFlorian Fainelli {
657318166cSFlorian Fainelli 	u32 offset = CORE_UDF_0_A_0_8_PORT_0 + slice_num * UDF_SLICE_OFFSET;
667318166cSFlorian Fainelli 	unsigned int i;
677318166cSFlorian Fainelli 
687318166cSFlorian Fainelli 	for (i = 0; i < UDF_NUM_SLICES; i++)
697318166cSFlorian Fainelli 		core_writel(priv, layout[i], offset + i * 4);
707318166cSFlorian Fainelli }
717318166cSFlorian Fainelli 
727318166cSFlorian Fainelli static int bcm_sf2_cfp_op(struct bcm_sf2_priv *priv, unsigned int op)
737318166cSFlorian Fainelli {
747318166cSFlorian Fainelli 	unsigned int timeout = 1000;
757318166cSFlorian Fainelli 	u32 reg;
767318166cSFlorian Fainelli 
777318166cSFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_ACC);
787318166cSFlorian Fainelli 	reg &= ~(OP_SEL_MASK | RAM_SEL_MASK);
797318166cSFlorian Fainelli 	reg |= OP_STR_DONE | op;
807318166cSFlorian Fainelli 	core_writel(priv, reg, CORE_CFP_ACC);
817318166cSFlorian Fainelli 
827318166cSFlorian Fainelli 	do {
837318166cSFlorian Fainelli 		reg = core_readl(priv, CORE_CFP_ACC);
847318166cSFlorian Fainelli 		if (!(reg & OP_STR_DONE))
857318166cSFlorian Fainelli 			break;
867318166cSFlorian Fainelli 
877318166cSFlorian Fainelli 		cpu_relax();
887318166cSFlorian Fainelli 	} while (timeout--);
897318166cSFlorian Fainelli 
907318166cSFlorian Fainelli 	if (!timeout)
917318166cSFlorian Fainelli 		return -ETIMEDOUT;
927318166cSFlorian Fainelli 
937318166cSFlorian Fainelli 	return 0;
947318166cSFlorian Fainelli }
957318166cSFlorian Fainelli 
967318166cSFlorian Fainelli static inline void bcm_sf2_cfp_rule_addr_set(struct bcm_sf2_priv *priv,
977318166cSFlorian Fainelli 					     unsigned int addr)
987318166cSFlorian Fainelli {
997318166cSFlorian Fainelli 	u32 reg;
1007318166cSFlorian Fainelli 
101df191632SFlorian Fainelli 	WARN_ON(addr >= priv->num_cfp_rules);
1027318166cSFlorian Fainelli 
1037318166cSFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_ACC);
1047318166cSFlorian Fainelli 	reg &= ~(XCESS_ADDR_MASK << XCESS_ADDR_SHIFT);
1057318166cSFlorian Fainelli 	reg |= addr << XCESS_ADDR_SHIFT;
1067318166cSFlorian Fainelli 	core_writel(priv, reg, CORE_CFP_ACC);
1077318166cSFlorian Fainelli }
1087318166cSFlorian Fainelli 
1097318166cSFlorian Fainelli static inline unsigned int bcm_sf2_cfp_rule_size(struct bcm_sf2_priv *priv)
1107318166cSFlorian Fainelli {
1117318166cSFlorian Fainelli 	/* Entry #0 is reserved */
112df191632SFlorian Fainelli 	return priv->num_cfp_rules - 1;
1137318166cSFlorian Fainelli }
1147318166cSFlorian Fainelli 
11533061458SFlorian Fainelli static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv,
11633061458SFlorian Fainelli 				   unsigned int rule_index,
11733061458SFlorian Fainelli 				   unsigned int port_num,
11833061458SFlorian Fainelli 				   unsigned int queue_num)
11933061458SFlorian Fainelli {
12033061458SFlorian Fainelli 	int ret;
12133061458SFlorian Fainelli 	u32 reg;
12233061458SFlorian Fainelli 
12333061458SFlorian Fainelli 	/* Replace ARL derived destination with DST_MAP derived, define
12433061458SFlorian Fainelli 	 * which port and queue this should be forwarded to.
12533061458SFlorian Fainelli 	 */
12633061458SFlorian Fainelli 	reg = CHANGE_FWRD_MAP_IB_REP_ARL | BIT(port_num + DST_MAP_IB_SHIFT) |
12733061458SFlorian Fainelli 		CHANGE_TC | queue_num << NEW_TC_SHIFT;
12833061458SFlorian Fainelli 
12933061458SFlorian Fainelli 	core_writel(priv, reg, CORE_ACT_POL_DATA0);
13033061458SFlorian Fainelli 
13133061458SFlorian Fainelli 	/* Set classification ID that needs to be put in Broadcom tag */
13233061458SFlorian Fainelli 	core_writel(priv, rule_index << CHAIN_ID_SHIFT,
13333061458SFlorian Fainelli 		    CORE_ACT_POL_DATA1);
13433061458SFlorian Fainelli 
13533061458SFlorian Fainelli 	core_writel(priv, 0, CORE_ACT_POL_DATA2);
13633061458SFlorian Fainelli 
13733061458SFlorian Fainelli 	/* Configure policer RAM now */
13833061458SFlorian Fainelli 	ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | ACT_POL_RAM);
13933061458SFlorian Fainelli 	if (ret) {
14033061458SFlorian Fainelli 		pr_err("Policer entry at %d failed\n", rule_index);
14133061458SFlorian Fainelli 		return ret;
14233061458SFlorian Fainelli 	}
14333061458SFlorian Fainelli 
14433061458SFlorian Fainelli 	/* Disable the policer */
14533061458SFlorian Fainelli 	core_writel(priv, POLICER_MODE_DISABLE, CORE_RATE_METER0);
14633061458SFlorian Fainelli 
14733061458SFlorian Fainelli 	/* Now the rate meter */
14833061458SFlorian Fainelli 	ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | RATE_METER_RAM);
14933061458SFlorian Fainelli 	if (ret) {
15033061458SFlorian Fainelli 		pr_err("Meter entry at %d failed\n", rule_index);
15133061458SFlorian Fainelli 		return ret;
15233061458SFlorian Fainelli 	}
15333061458SFlorian Fainelli 
15433061458SFlorian Fainelli 	return 0;
15533061458SFlorian Fainelli }
15633061458SFlorian Fainelli 
15733061458SFlorian Fainelli static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
15833061458SFlorian Fainelli 				     unsigned int port_num,
15933061458SFlorian Fainelli 				     unsigned int queue_num,
1607318166cSFlorian Fainelli 				     struct ethtool_rx_flow_spec *fs)
1617318166cSFlorian Fainelli {
1627318166cSFlorian Fainelli 	const struct cfp_udf_layout *layout;
16333061458SFlorian Fainelli 	struct ethtool_tcpip4_spec *v4_spec;
1647318166cSFlorian Fainelli 	unsigned int slice_num, rule_index;
1657318166cSFlorian Fainelli 	u8 ip_proto, ip_frag;
1667318166cSFlorian Fainelli 	u8 num_udf;
1677318166cSFlorian Fainelli 	u32 reg;
1687318166cSFlorian Fainelli 	int ret;
1697318166cSFlorian Fainelli 
1707318166cSFlorian Fainelli 	switch (fs->flow_type & ~FLOW_EXT) {
1717318166cSFlorian Fainelli 	case TCP_V4_FLOW:
1727318166cSFlorian Fainelli 		ip_proto = IPPROTO_TCP;
1737318166cSFlorian Fainelli 		v4_spec = &fs->h_u.tcp_ip4_spec;
1747318166cSFlorian Fainelli 		break;
1757318166cSFlorian Fainelli 	case UDP_V4_FLOW:
1767318166cSFlorian Fainelli 		ip_proto = IPPROTO_UDP;
1777318166cSFlorian Fainelli 		v4_spec = &fs->h_u.udp_ip4_spec;
1787318166cSFlorian Fainelli 		break;
1797318166cSFlorian Fainelli 	default:
1807318166cSFlorian Fainelli 		return -EINVAL;
1817318166cSFlorian Fainelli 	}
1827318166cSFlorian Fainelli 
18333061458SFlorian Fainelli 	ip_frag = be32_to_cpu(fs->m_ext.data[0]);
18433061458SFlorian Fainelli 
18533061458SFlorian Fainelli 	/* Locate the first rule available */
18633061458SFlorian Fainelli 	if (fs->location == RX_CLS_LOC_ANY)
18733061458SFlorian Fainelli 		rule_index = find_first_zero_bit(priv->cfp.used,
18833061458SFlorian Fainelli 						 bcm_sf2_cfp_rule_size(priv));
18933061458SFlorian Fainelli 	else
19033061458SFlorian Fainelli 		rule_index = fs->location;
19133061458SFlorian Fainelli 
1927318166cSFlorian Fainelli 	/* We only use one UDF slice for now */
1937318166cSFlorian Fainelli 	slice_num = 1;
1947318166cSFlorian Fainelli 	layout = &udf_tcpip4_layout;
1957318166cSFlorian Fainelli 	num_udf = bcm_sf2_get_num_udf_slices(layout->slices);
1967318166cSFlorian Fainelli 
1977318166cSFlorian Fainelli 	/* Apply the UDF layout for this filter */
1987318166cSFlorian Fainelli 	bcm_sf2_cfp_udf_set(priv, slice_num, layout->slices);
1997318166cSFlorian Fainelli 
2007318166cSFlorian Fainelli 	/* Apply to all packets received through this port */
2017318166cSFlorian Fainelli 	core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7));
2027318166cSFlorian Fainelli 
20333061458SFlorian Fainelli 	/* Source port map match */
20433061458SFlorian Fainelli 	core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7));
20533061458SFlorian Fainelli 
2067318166cSFlorian Fainelli 	/* S-Tag status		[31:30]
2077318166cSFlorian Fainelli 	 * C-Tag status		[29:28]
2087318166cSFlorian Fainelli 	 * L2 framing		[27:26]
2097318166cSFlorian Fainelli 	 * L3 framing		[25:24]
2107318166cSFlorian Fainelli 	 * IP ToS		[23:16]
2117318166cSFlorian Fainelli 	 * IP proto		[15:08]
2127318166cSFlorian Fainelli 	 * IP Fragm		[7]
2137318166cSFlorian Fainelli 	 * Non 1st frag		[6]
2147318166cSFlorian Fainelli 	 * IP Authen		[5]
2157318166cSFlorian Fainelli 	 * TTL range		[4:3]
2167318166cSFlorian Fainelli 	 * PPPoE session	[2]
2177318166cSFlorian Fainelli 	 * Reserved		[1]
2187318166cSFlorian Fainelli 	 * UDF_Valid[8]		[0]
2197318166cSFlorian Fainelli 	 */
22039cdd349SFlorian Fainelli 	core_writel(priv, v4_spec->tos << IPTOS_SHIFT |
22139cdd349SFlorian Fainelli 		    ip_proto << IPPROTO_SHIFT | ip_frag << IP_FRAG_SHIFT,
2227318166cSFlorian Fainelli 		    CORE_CFP_DATA_PORT(6));
2237318166cSFlorian Fainelli 
2247318166cSFlorian Fainelli 	/* UDF_Valid[7:0]	[31:24]
2257318166cSFlorian Fainelli 	 * S-Tag		[23:8]
2267318166cSFlorian Fainelli 	 * C-Tag		[7:0]
2277318166cSFlorian Fainelli 	 */
2287318166cSFlorian Fainelli 	core_writel(priv, GENMASK(num_udf - 1, 0) << 24, CORE_CFP_DATA_PORT(5));
2297318166cSFlorian Fainelli 
2307318166cSFlorian Fainelli 	/* C-Tag		[31:24]
2317318166cSFlorian Fainelli 	 * UDF_n_A8		[23:8]
2327318166cSFlorian Fainelli 	 * UDF_n_A7		[7:0]
2337318166cSFlorian Fainelli 	 */
2347318166cSFlorian Fainelli 	core_writel(priv, 0, CORE_CFP_DATA_PORT(4));
2357318166cSFlorian Fainelli 
2367318166cSFlorian Fainelli 	/* UDF_n_A7		[31:24]
2377318166cSFlorian Fainelli 	 * UDF_n_A6		[23:8]
2387318166cSFlorian Fainelli 	 * UDF_n_A5		[7:0]
2397318166cSFlorian Fainelli 	 */
2407318166cSFlorian Fainelli 	core_writel(priv, be16_to_cpu(v4_spec->pdst) >> 8,
2417318166cSFlorian Fainelli 		    CORE_CFP_DATA_PORT(3));
2427318166cSFlorian Fainelli 
2437318166cSFlorian Fainelli 	/* UDF_n_A5		[31:24]
2447318166cSFlorian Fainelli 	 * UDF_n_A4		[23:8]
2457318166cSFlorian Fainelli 	 * UDF_n_A3		[7:0]
2467318166cSFlorian Fainelli 	 */
2477318166cSFlorian Fainelli 	reg = (be16_to_cpu(v4_spec->pdst) & 0xff) << 24 |
2487318166cSFlorian Fainelli 	      (u32)be16_to_cpu(v4_spec->psrc) << 8 |
2497318166cSFlorian Fainelli 	      (be32_to_cpu(v4_spec->ip4dst) & 0x0000ff00) >> 8;
2507318166cSFlorian Fainelli 	core_writel(priv, reg, CORE_CFP_DATA_PORT(2));
2517318166cSFlorian Fainelli 
2527318166cSFlorian Fainelli 	/* UDF_n_A3		[31:24]
2537318166cSFlorian Fainelli 	 * UDF_n_A2		[23:8]
2547318166cSFlorian Fainelli 	 * UDF_n_A1		[7:0]
2557318166cSFlorian Fainelli 	 */
2567318166cSFlorian Fainelli 	reg = (u32)(be32_to_cpu(v4_spec->ip4dst) & 0xff) << 24 |
2577318166cSFlorian Fainelli 	      (u32)(be32_to_cpu(v4_spec->ip4dst) >> 16) << 8 |
2587318166cSFlorian Fainelli 	      (be32_to_cpu(v4_spec->ip4src) & 0x0000ff00) >> 8;
2597318166cSFlorian Fainelli 	core_writel(priv, reg, CORE_CFP_DATA_PORT(1));
2607318166cSFlorian Fainelli 
2617318166cSFlorian Fainelli 	/* UDF_n_A1		[31:24]
2627318166cSFlorian Fainelli 	 * UDF_n_A0		[23:8]
2637318166cSFlorian Fainelli 	 * Reserved		[7:4]
2647318166cSFlorian Fainelli 	 * Slice ID		[3:2]
2657318166cSFlorian Fainelli 	 * Slice valid		[1:0]
2667318166cSFlorian Fainelli 	 */
2677318166cSFlorian Fainelli 	reg = (u32)(be32_to_cpu(v4_spec->ip4src) & 0xff) << 24 |
2687318166cSFlorian Fainelli 	      (u32)(be32_to_cpu(v4_spec->ip4src) >> 16) << 8 |
2697318166cSFlorian Fainelli 	      SLICE_NUM(slice_num) | SLICE_VALID;
2707318166cSFlorian Fainelli 	core_writel(priv, reg, CORE_CFP_DATA_PORT(0));
2717318166cSFlorian Fainelli 
2727318166cSFlorian Fainelli 	/* Mask with the specific layout for IPv4 packets */
2737318166cSFlorian Fainelli 	core_writel(priv, layout->mask_value, CORE_CFP_MASK_PORT(6));
2747318166cSFlorian Fainelli 
2757318166cSFlorian Fainelli 	/* Mask all but valid UDFs */
2767318166cSFlorian Fainelli 	core_writel(priv, GENMASK(num_udf - 1, 0) << 24, CORE_CFP_MASK_PORT(5));
2777318166cSFlorian Fainelli 
2787318166cSFlorian Fainelli 	/* Mask all */
2797318166cSFlorian Fainelli 	core_writel(priv, 0, CORE_CFP_MASK_PORT(4));
2807318166cSFlorian Fainelli 
2817318166cSFlorian Fainelli 	/* All other UDFs should be matched with the filter */
2827318166cSFlorian Fainelli 	core_writel(priv, 0xff, CORE_CFP_MASK_PORT(3));
2837318166cSFlorian Fainelli 	core_writel(priv, 0xffffffff, CORE_CFP_MASK_PORT(2));
2847318166cSFlorian Fainelli 	core_writel(priv, 0xffffffff, CORE_CFP_MASK_PORT(1));
2857318166cSFlorian Fainelli 	core_writel(priv, 0xffffff0f, CORE_CFP_MASK_PORT(0));
2867318166cSFlorian Fainelli 
2877318166cSFlorian Fainelli 	/* Insert into TCAM now */
2887318166cSFlorian Fainelli 	bcm_sf2_cfp_rule_addr_set(priv, rule_index);
2897318166cSFlorian Fainelli 
2907318166cSFlorian Fainelli 	ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);
2917318166cSFlorian Fainelli 	if (ret) {
2927318166cSFlorian Fainelli 		pr_err("TCAM entry at addr %d failed\n", rule_index);
2937318166cSFlorian Fainelli 		return ret;
2947318166cSFlorian Fainelli 	}
2957318166cSFlorian Fainelli 
29633061458SFlorian Fainelli 	/* Insert into Action and policer RAMs now */
29733061458SFlorian Fainelli 	ret = bcm_sf2_cfp_act_pol_set(priv, rule_index, port_num, queue_num);
29833061458SFlorian Fainelli 	if (ret)
2997318166cSFlorian Fainelli 		return ret;
3007318166cSFlorian Fainelli 
3017318166cSFlorian Fainelli 	/* Turn on CFP for this rule now */
3027318166cSFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_CTL_REG);
3037318166cSFlorian Fainelli 	reg |= BIT(port);
3047318166cSFlorian Fainelli 	core_writel(priv, reg, CORE_CFP_CTL_REG);
3057318166cSFlorian Fainelli 
3067318166cSFlorian Fainelli 	/* Flag the rule as being used and return it */
3077318166cSFlorian Fainelli 	set_bit(rule_index, priv->cfp.used);
3087318166cSFlorian Fainelli 	fs->location = rule_index;
3097318166cSFlorian Fainelli 
3107318166cSFlorian Fainelli 	return 0;
3117318166cSFlorian Fainelli }
3127318166cSFlorian Fainelli 
31333061458SFlorian Fainelli static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
31433061458SFlorian Fainelli 				struct ethtool_rx_flow_spec *fs)
31533061458SFlorian Fainelli {
31633061458SFlorian Fainelli 	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
31733061458SFlorian Fainelli 	unsigned int queue_num, port_num;
31833061458SFlorian Fainelli 	int ret;
31933061458SFlorian Fainelli 
32033061458SFlorian Fainelli 	/* Check for unsupported extensions */
32133061458SFlorian Fainelli 	if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype ||
32233061458SFlorian Fainelli 	     fs->m_ext.data[1]))
32333061458SFlorian Fainelli 		return -EINVAL;
32433061458SFlorian Fainelli 
32533061458SFlorian Fainelli 	if (fs->location != RX_CLS_LOC_ANY &&
32633061458SFlorian Fainelli 	    test_bit(fs->location, priv->cfp.used))
32733061458SFlorian Fainelli 		return -EBUSY;
32833061458SFlorian Fainelli 
32933061458SFlorian Fainelli 	if (fs->location != RX_CLS_LOC_ANY &&
33033061458SFlorian Fainelli 	    fs->location > bcm_sf2_cfp_rule_size(priv))
33133061458SFlorian Fainelli 		return -EINVAL;
33233061458SFlorian Fainelli 
33333061458SFlorian Fainelli 	/* We do not support discarding packets, check that the
33433061458SFlorian Fainelli 	 * destination port is enabled and that we are within the
33533061458SFlorian Fainelli 	 * number of ports supported by the switch
33633061458SFlorian Fainelli 	 */
33733061458SFlorian Fainelli 	port_num = fs->ring_cookie / SF2_NUM_EGRESS_QUEUES;
33833061458SFlorian Fainelli 
33933061458SFlorian Fainelli 	if (fs->ring_cookie == RX_CLS_FLOW_DISC ||
34033061458SFlorian Fainelli 	    !(BIT(port_num) & ds->enabled_port_mask) ||
34133061458SFlorian Fainelli 	    port_num >= priv->hw_params.num_ports)
34233061458SFlorian Fainelli 		return -EINVAL;
34333061458SFlorian Fainelli 	/*
34433061458SFlorian Fainelli 	 * We have a small oddity where Port 6 just does not have a
34533061458SFlorian Fainelli 	 * valid bit here (so we substract by one).
34633061458SFlorian Fainelli 	 */
34733061458SFlorian Fainelli 	queue_num = fs->ring_cookie % SF2_NUM_EGRESS_QUEUES;
34833061458SFlorian Fainelli 	if (port_num >= 7)
34933061458SFlorian Fainelli 		port_num -= 1;
35033061458SFlorian Fainelli 
35133061458SFlorian Fainelli 	ret = bcm_sf2_cfp_ipv4_rule_set(priv, port, port_num, queue_num, fs);
35233061458SFlorian Fainelli 	if (ret)
35333061458SFlorian Fainelli 		return ret;
35433061458SFlorian Fainelli 
35533061458SFlorian Fainelli 	return 0;
35633061458SFlorian Fainelli }
35733061458SFlorian Fainelli 
3587318166cSFlorian Fainelli static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port,
3597318166cSFlorian Fainelli 				u32 loc)
3607318166cSFlorian Fainelli {
3617318166cSFlorian Fainelli 	int ret;
3627318166cSFlorian Fainelli 	u32 reg;
3637318166cSFlorian Fainelli 
3647318166cSFlorian Fainelli 	/* Refuse deletion of unused rules, and the default reserved rule */
3657318166cSFlorian Fainelli 	if (!test_bit(loc, priv->cfp.used) || loc == 0)
3667318166cSFlorian Fainelli 		return -EINVAL;
3677318166cSFlorian Fainelli 
3687318166cSFlorian Fainelli 	/* Indicate which rule we want to read */
3697318166cSFlorian Fainelli 	bcm_sf2_cfp_rule_addr_set(priv, loc);
3707318166cSFlorian Fainelli 
3717318166cSFlorian Fainelli 	ret =  bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL);
3727318166cSFlorian Fainelli 	if (ret)
3737318166cSFlorian Fainelli 		return ret;
3747318166cSFlorian Fainelli 
3757318166cSFlorian Fainelli 	/* Clear its valid bits */
3767318166cSFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_DATA_PORT(0));
3777318166cSFlorian Fainelli 	reg &= ~SLICE_VALID;
3787318166cSFlorian Fainelli 	core_writel(priv, reg, CORE_CFP_DATA_PORT(0));
3797318166cSFlorian Fainelli 
3807318166cSFlorian Fainelli 	/* Write back this entry into the TCAM now */
3817318166cSFlorian Fainelli 	ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);
3827318166cSFlorian Fainelli 	if (ret)
3837318166cSFlorian Fainelli 		return ret;
3847318166cSFlorian Fainelli 
3857318166cSFlorian Fainelli 	clear_bit(loc, priv->cfp.used);
3867318166cSFlorian Fainelli 
3877318166cSFlorian Fainelli 	return 0;
3887318166cSFlorian Fainelli }
3897318166cSFlorian Fainelli 
3907318166cSFlorian Fainelli static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow)
3917318166cSFlorian Fainelli {
3927318166cSFlorian Fainelli 	unsigned int i;
3937318166cSFlorian Fainelli 
3947318166cSFlorian Fainelli 	for (i = 0; i < sizeof(flow->m_u); i++)
3957318166cSFlorian Fainelli 		flow->m_u.hdata[i] ^= 0xff;
3967318166cSFlorian Fainelli 
3977318166cSFlorian Fainelli 	flow->m_ext.vlan_etype ^= cpu_to_be16(~0);
3987318166cSFlorian Fainelli 	flow->m_ext.vlan_tci ^= cpu_to_be16(~0);
3997318166cSFlorian Fainelli 	flow->m_ext.data[0] ^= cpu_to_be32(~0);
4007318166cSFlorian Fainelli 	flow->m_ext.data[1] ^= cpu_to_be32(~0);
4017318166cSFlorian Fainelli }
4027318166cSFlorian Fainelli 
40333061458SFlorian Fainelli static int bcm_sf2_cfp_ipv4_rule_get(struct bcm_sf2_priv *priv, int port,
40433061458SFlorian Fainelli 				     struct ethtool_tcpip4_spec *v4_spec,
40533061458SFlorian Fainelli 				     struct ethtool_tcpip4_spec *v4_m_spec)
40633061458SFlorian Fainelli {
40733061458SFlorian Fainelli 	u16 src_dst_port;
40833061458SFlorian Fainelli 	u32 reg, ipv4;
40933061458SFlorian Fainelli 
41033061458SFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_DATA_PORT(3));
41133061458SFlorian Fainelli 	/* src port [15:8] */
41233061458SFlorian Fainelli 	src_dst_port = reg << 8;
41333061458SFlorian Fainelli 
41433061458SFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_DATA_PORT(2));
41533061458SFlorian Fainelli 	/* src port [7:0] */
41633061458SFlorian Fainelli 	src_dst_port |= (reg >> 24);
41733061458SFlorian Fainelli 
41833061458SFlorian Fainelli 	v4_spec->pdst = cpu_to_be16(src_dst_port);
41933061458SFlorian Fainelli 	v4_m_spec->pdst = cpu_to_be16(~0);
42033061458SFlorian Fainelli 	v4_spec->psrc = cpu_to_be16((u16)(reg >> 8));
42133061458SFlorian Fainelli 	v4_m_spec->psrc = cpu_to_be16(~0);
42233061458SFlorian Fainelli 
42333061458SFlorian Fainelli 	/* IPv4 dst [15:8] */
42433061458SFlorian Fainelli 	ipv4 = (reg & 0xff) << 8;
42533061458SFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_DATA_PORT(1));
42633061458SFlorian Fainelli 	/* IPv4 dst [31:16] */
42733061458SFlorian Fainelli 	ipv4 |= ((reg >> 8) & 0xffff) << 16;
42833061458SFlorian Fainelli 	/* IPv4 dst [7:0] */
42933061458SFlorian Fainelli 	ipv4 |= (reg >> 24) & 0xff;
43033061458SFlorian Fainelli 	v4_spec->ip4dst = cpu_to_be32(ipv4);
43133061458SFlorian Fainelli 	v4_m_spec->ip4dst = cpu_to_be32(~0);
43233061458SFlorian Fainelli 
43333061458SFlorian Fainelli 	/* IPv4 src [15:8] */
43433061458SFlorian Fainelli 	ipv4 = (reg & 0xff) << 8;
43533061458SFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_DATA_PORT(0));
43633061458SFlorian Fainelli 
43733061458SFlorian Fainelli 	if (!(reg & SLICE_VALID))
43833061458SFlorian Fainelli 		return -EINVAL;
43933061458SFlorian Fainelli 
44033061458SFlorian Fainelli 	/* IPv4 src [7:0] */
44133061458SFlorian Fainelli 	ipv4 |= (reg >> 24) & 0xff;
44233061458SFlorian Fainelli 	/* IPv4 src [31:16] */
44333061458SFlorian Fainelli 	ipv4 |= ((reg >> 8) & 0xffff) << 16;
44433061458SFlorian Fainelli 	v4_spec->ip4src = cpu_to_be32(ipv4);
44533061458SFlorian Fainelli 	v4_m_spec->ip4src = cpu_to_be32(~0);
44633061458SFlorian Fainelli 
44733061458SFlorian Fainelli 	return 0;
44833061458SFlorian Fainelli }
44933061458SFlorian Fainelli 
4507318166cSFlorian Fainelli static int bcm_sf2_cfp_rule_get(struct bcm_sf2_priv *priv, int port,
4517318166cSFlorian Fainelli 				struct ethtool_rxnfc *nfc, bool search)
4527318166cSFlorian Fainelli {
45333061458SFlorian Fainelli 	struct ethtool_tcpip4_spec *v4_spec = NULL, *v4_m_spec;
4547318166cSFlorian Fainelli 	unsigned int queue_num;
45533061458SFlorian Fainelli 	u32 reg;
4567318166cSFlorian Fainelli 	int ret;
4577318166cSFlorian Fainelli 
4587318166cSFlorian Fainelli 	if (!search) {
4597318166cSFlorian Fainelli 		bcm_sf2_cfp_rule_addr_set(priv, nfc->fs.location);
4607318166cSFlorian Fainelli 
4617318166cSFlorian Fainelli 		ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | ACT_POL_RAM);
4627318166cSFlorian Fainelli 		if (ret)
4637318166cSFlorian Fainelli 			return ret;
4647318166cSFlorian Fainelli 
4657318166cSFlorian Fainelli 		reg = core_readl(priv, CORE_ACT_POL_DATA0);
4667318166cSFlorian Fainelli 
4677318166cSFlorian Fainelli 		ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL);
4687318166cSFlorian Fainelli 		if (ret)
4697318166cSFlorian Fainelli 			return ret;
4707318166cSFlorian Fainelli 	} else {
4717318166cSFlorian Fainelli 		reg = core_readl(priv, CORE_ACT_POL_DATA0);
4727318166cSFlorian Fainelli 	}
4737318166cSFlorian Fainelli 
4747318166cSFlorian Fainelli 	/* Extract the destination port */
4757318166cSFlorian Fainelli 	nfc->fs.ring_cookie = fls((reg >> DST_MAP_IB_SHIFT) &
4767318166cSFlorian Fainelli 				  DST_MAP_IB_MASK) - 1;
4777318166cSFlorian Fainelli 
4787318166cSFlorian Fainelli 	/* There is no Port 6, so we compensate for that here */
4797318166cSFlorian Fainelli 	if (nfc->fs.ring_cookie >= 6)
4807318166cSFlorian Fainelli 		nfc->fs.ring_cookie++;
481152b6fd6SFlorian Fainelli 	nfc->fs.ring_cookie *= SF2_NUM_EGRESS_QUEUES;
4827318166cSFlorian Fainelli 
4837318166cSFlorian Fainelli 	/* Extract the destination queue */
4847318166cSFlorian Fainelli 	queue_num = (reg >> NEW_TC_SHIFT) & NEW_TC_MASK;
4857318166cSFlorian Fainelli 	nfc->fs.ring_cookie += queue_num;
4867318166cSFlorian Fainelli 
4877318166cSFlorian Fainelli 	/* Extract the IP protocol */
4887318166cSFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_DATA_PORT(6));
4897318166cSFlorian Fainelli 	switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) {
4907318166cSFlorian Fainelli 	case IPPROTO_TCP:
4917318166cSFlorian Fainelli 		nfc->fs.flow_type = TCP_V4_FLOW;
4927318166cSFlorian Fainelli 		v4_spec = &nfc->fs.h_u.tcp_ip4_spec;
49333061458SFlorian Fainelli 		v4_m_spec = &nfc->fs.m_u.tcp_ip4_spec;
4947318166cSFlorian Fainelli 		break;
4957318166cSFlorian Fainelli 	case IPPROTO_UDP:
4967318166cSFlorian Fainelli 		nfc->fs.flow_type = UDP_V4_FLOW;
4977318166cSFlorian Fainelli 		v4_spec = &nfc->fs.h_u.udp_ip4_spec;
49833061458SFlorian Fainelli 		v4_m_spec = &nfc->fs.m_u.udp_ip4_spec;
4997318166cSFlorian Fainelli 		break;
5007318166cSFlorian Fainelli 	default:
5017318166cSFlorian Fainelli 		/* Clear to exit the search process */
5027318166cSFlorian Fainelli 		if (search)
5037318166cSFlorian Fainelli 			core_readl(priv, CORE_CFP_DATA_PORT(7));
5047318166cSFlorian Fainelli 		return -EINVAL;
5057318166cSFlorian Fainelli 	}
5067318166cSFlorian Fainelli 
50739cdd349SFlorian Fainelli 	nfc->fs.m_ext.data[0] = cpu_to_be32((reg >> IP_FRAG_SHIFT) & 1);
50833061458SFlorian Fainelli 	if (v4_spec) {
50933061458SFlorian Fainelli 		v4_spec->tos = (reg >> IPTOS_SHIFT) & IPTOS_MASK;
51033061458SFlorian Fainelli 		ret = bcm_sf2_cfp_ipv4_rule_get(priv, port, v4_spec, v4_m_spec);
51133061458SFlorian Fainelli 	}
5127318166cSFlorian Fainelli 
51333061458SFlorian Fainelli 	if (ret)
51433061458SFlorian Fainelli 		return ret;
5157318166cSFlorian Fainelli 
5167318166cSFlorian Fainelli 	/* Read last to avoid next entry clobbering the results during search
5177318166cSFlorian Fainelli 	 * operations
5187318166cSFlorian Fainelli 	 */
5197318166cSFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_DATA_PORT(7));
5207318166cSFlorian Fainelli 	if (!(reg & 1 << port))
5217318166cSFlorian Fainelli 		return -EINVAL;
5227318166cSFlorian Fainelli 
5237318166cSFlorian Fainelli 	bcm_sf2_invert_masks(&nfc->fs);
5247318166cSFlorian Fainelli 
5257318166cSFlorian Fainelli 	/* Put the TCAM size here */
5267318166cSFlorian Fainelli 	nfc->data = bcm_sf2_cfp_rule_size(priv);
5277318166cSFlorian Fainelli 
5287318166cSFlorian Fainelli 	return 0;
5297318166cSFlorian Fainelli }
5307318166cSFlorian Fainelli 
5317318166cSFlorian Fainelli /* We implement the search doing a TCAM search operation */
5327318166cSFlorian Fainelli static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv,
5337318166cSFlorian Fainelli 				    int port, struct ethtool_rxnfc *nfc,
5347318166cSFlorian Fainelli 				    u32 *rule_locs)
5357318166cSFlorian Fainelli {
5367318166cSFlorian Fainelli 	unsigned int index = 1, rules_cnt = 0;
5377318166cSFlorian Fainelli 	int ret;
5387318166cSFlorian Fainelli 	u32 reg;
5397318166cSFlorian Fainelli 
5407318166cSFlorian Fainelli 	/* Do not poll on OP_STR_DONE to be self-clearing for search
5417318166cSFlorian Fainelli 	 * operations, we cannot use bcm_sf2_cfp_op here because it completes
5427318166cSFlorian Fainelli 	 * on clearing OP_STR_DONE which won't clear until the entire search
5437318166cSFlorian Fainelli 	 * operation is over.
5447318166cSFlorian Fainelli 	 */
5457318166cSFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_ACC);
5467318166cSFlorian Fainelli 	reg &= ~(XCESS_ADDR_MASK << XCESS_ADDR_SHIFT);
5477318166cSFlorian Fainelli 	reg |= index << XCESS_ADDR_SHIFT;
5487318166cSFlorian Fainelli 	reg &= ~(OP_SEL_MASK | RAM_SEL_MASK);
5497318166cSFlorian Fainelli 	reg |= OP_SEL_SEARCH | TCAM_SEL | OP_STR_DONE;
5507318166cSFlorian Fainelli 	core_writel(priv, reg, CORE_CFP_ACC);
5517318166cSFlorian Fainelli 
5527318166cSFlorian Fainelli 	do {
5537318166cSFlorian Fainelli 		/* Wait for results to be ready */
5547318166cSFlorian Fainelli 		reg = core_readl(priv, CORE_CFP_ACC);
5557318166cSFlorian Fainelli 
5567318166cSFlorian Fainelli 		/* Extract the address we are searching */
5577318166cSFlorian Fainelli 		index = reg >> XCESS_ADDR_SHIFT;
5587318166cSFlorian Fainelli 		index &= XCESS_ADDR_MASK;
5597318166cSFlorian Fainelli 
5607318166cSFlorian Fainelli 		/* We have a valid search result, so flag it accordingly */
5617318166cSFlorian Fainelli 		if (reg & SEARCH_STS) {
5627318166cSFlorian Fainelli 			ret = bcm_sf2_cfp_rule_get(priv, port, nfc, true);
5637318166cSFlorian Fainelli 			if (ret)
5647318166cSFlorian Fainelli 				continue;
5657318166cSFlorian Fainelli 
5667318166cSFlorian Fainelli 			rule_locs[rules_cnt] = index;
5677318166cSFlorian Fainelli 			rules_cnt++;
5687318166cSFlorian Fainelli 		}
5697318166cSFlorian Fainelli 
5707318166cSFlorian Fainelli 		/* Search is over break out */
5717318166cSFlorian Fainelli 		if (!(reg & OP_STR_DONE))
5727318166cSFlorian Fainelli 			break;
5737318166cSFlorian Fainelli 
574df191632SFlorian Fainelli 	} while (index < priv->num_cfp_rules);
5757318166cSFlorian Fainelli 
5767318166cSFlorian Fainelli 	/* Put the TCAM size here */
5777318166cSFlorian Fainelli 	nfc->data = bcm_sf2_cfp_rule_size(priv);
5787318166cSFlorian Fainelli 	nfc->rule_cnt = rules_cnt;
5797318166cSFlorian Fainelli 
5807318166cSFlorian Fainelli 	return 0;
5817318166cSFlorian Fainelli }
5827318166cSFlorian Fainelli 
5837318166cSFlorian Fainelli int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
5847318166cSFlorian Fainelli 		      struct ethtool_rxnfc *nfc, u32 *rule_locs)
5857318166cSFlorian Fainelli {
5867318166cSFlorian Fainelli 	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
5877318166cSFlorian Fainelli 	int ret = 0;
5887318166cSFlorian Fainelli 
5897318166cSFlorian Fainelli 	mutex_lock(&priv->cfp.lock);
5907318166cSFlorian Fainelli 
5917318166cSFlorian Fainelli 	switch (nfc->cmd) {
5927318166cSFlorian Fainelli 	case ETHTOOL_GRXCLSRLCNT:
5937318166cSFlorian Fainelli 		/* Subtract the default, unusable rule */
5947318166cSFlorian Fainelli 		nfc->rule_cnt = bitmap_weight(priv->cfp.used,
595df191632SFlorian Fainelli 					      priv->num_cfp_rules) - 1;
5967318166cSFlorian Fainelli 		/* We support specifying rule locations */
5977318166cSFlorian Fainelli 		nfc->data |= RX_CLS_LOC_SPECIAL;
5987318166cSFlorian Fainelli 		break;
5997318166cSFlorian Fainelli 	case ETHTOOL_GRXCLSRULE:
6007318166cSFlorian Fainelli 		ret = bcm_sf2_cfp_rule_get(priv, port, nfc, false);
6017318166cSFlorian Fainelli 		break;
6027318166cSFlorian Fainelli 	case ETHTOOL_GRXCLSRLALL:
6037318166cSFlorian Fainelli 		ret = bcm_sf2_cfp_rule_get_all(priv, port, nfc, rule_locs);
6047318166cSFlorian Fainelli 		break;
6057318166cSFlorian Fainelli 	default:
6067318166cSFlorian Fainelli 		ret = -EOPNOTSUPP;
6077318166cSFlorian Fainelli 		break;
6087318166cSFlorian Fainelli 	}
6097318166cSFlorian Fainelli 
6107318166cSFlorian Fainelli 	mutex_unlock(&priv->cfp.lock);
6117318166cSFlorian Fainelli 
6127318166cSFlorian Fainelli 	return ret;
6137318166cSFlorian Fainelli }
6147318166cSFlorian Fainelli 
6157318166cSFlorian Fainelli int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
6167318166cSFlorian Fainelli 		      struct ethtool_rxnfc *nfc)
6177318166cSFlorian Fainelli {
6187318166cSFlorian Fainelli 	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
6197318166cSFlorian Fainelli 	int ret = 0;
6207318166cSFlorian Fainelli 
6217318166cSFlorian Fainelli 	mutex_lock(&priv->cfp.lock);
6227318166cSFlorian Fainelli 
6237318166cSFlorian Fainelli 	switch (nfc->cmd) {
6247318166cSFlorian Fainelli 	case ETHTOOL_SRXCLSRLINS:
6257318166cSFlorian Fainelli 		ret = bcm_sf2_cfp_rule_set(ds, port, &nfc->fs);
6267318166cSFlorian Fainelli 		break;
6277318166cSFlorian Fainelli 
6287318166cSFlorian Fainelli 	case ETHTOOL_SRXCLSRLDEL:
6297318166cSFlorian Fainelli 		ret = bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location);
6307318166cSFlorian Fainelli 		break;
6317318166cSFlorian Fainelli 	default:
6327318166cSFlorian Fainelli 		ret = -EOPNOTSUPP;
6337318166cSFlorian Fainelli 		break;
6347318166cSFlorian Fainelli 	}
6357318166cSFlorian Fainelli 
6367318166cSFlorian Fainelli 	mutex_unlock(&priv->cfp.lock);
6377318166cSFlorian Fainelli 
6387318166cSFlorian Fainelli 	return ret;
6397318166cSFlorian Fainelli }
6407318166cSFlorian Fainelli 
6417318166cSFlorian Fainelli int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv)
6427318166cSFlorian Fainelli {
6437318166cSFlorian Fainelli 	unsigned int timeout = 1000;
6447318166cSFlorian Fainelli 	u32 reg;
6457318166cSFlorian Fainelli 
6467318166cSFlorian Fainelli 	reg = core_readl(priv, CORE_CFP_ACC);
6477318166cSFlorian Fainelli 	reg |= TCAM_RESET;
6487318166cSFlorian Fainelli 	core_writel(priv, reg, CORE_CFP_ACC);
6497318166cSFlorian Fainelli 
6507318166cSFlorian Fainelli 	do {
6517318166cSFlorian Fainelli 		reg = core_readl(priv, CORE_CFP_ACC);
6527318166cSFlorian Fainelli 		if (!(reg & TCAM_RESET))
6537318166cSFlorian Fainelli 			break;
6547318166cSFlorian Fainelli 
6557318166cSFlorian Fainelli 		cpu_relax();
6567318166cSFlorian Fainelli 	} while (timeout--);
6577318166cSFlorian Fainelli 
6587318166cSFlorian Fainelli 	if (!timeout)
6597318166cSFlorian Fainelli 		return -ETIMEDOUT;
6607318166cSFlorian Fainelli 
6617318166cSFlorian Fainelli 	return 0;
6627318166cSFlorian Fainelli }
663