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> 19e4f7ef54SPablo Neira Ayuso #include <net/flow_offload.h> 207318166cSFlorian Fainelli 217318166cSFlorian Fainelli #include "bcm_sf2.h" 227318166cSFlorian Fainelli #include "bcm_sf2_regs.h" 237318166cSFlorian Fainelli 24ae7a5affSFlorian Fainelli struct cfp_rule { 25ae7a5affSFlorian Fainelli int port; 26ae7a5affSFlorian Fainelli struct ethtool_rx_flow_spec fs; 27ae7a5affSFlorian Fainelli struct list_head next; 28ae7a5affSFlorian Fainelli }; 29ae7a5affSFlorian Fainelli 305d80bcbbSFlorian Fainelli struct cfp_udf_slice_layout { 315d80bcbbSFlorian Fainelli u8 slices[UDFS_PER_SLICE]; 327318166cSFlorian Fainelli u32 mask_value; 335d80bcbbSFlorian Fainelli u32 base_offset; 347318166cSFlorian Fainelli }; 357318166cSFlorian Fainelli 365d80bcbbSFlorian Fainelli struct cfp_udf_layout { 375d80bcbbSFlorian Fainelli struct cfp_udf_slice_layout udfs[UDF_NUM_SLICES]; 385d80bcbbSFlorian Fainelli }; 395d80bcbbSFlorian Fainelli 405d80bcbbSFlorian Fainelli static const u8 zero_slice[UDFS_PER_SLICE] = { }; 415d80bcbbSFlorian Fainelli 427318166cSFlorian Fainelli /* UDF slices layout for a TCPv4/UDPv4 specification */ 437318166cSFlorian Fainelli static const struct cfp_udf_layout udf_tcpip4_layout = { 445d80bcbbSFlorian Fainelli .udfs = { 455d80bcbbSFlorian Fainelli [1] = { 467318166cSFlorian Fainelli .slices = { 477318166cSFlorian Fainelli /* End of L2, byte offset 12, src IP[0:15] */ 487318166cSFlorian Fainelli CFG_UDF_EOL2 | 6, 497318166cSFlorian Fainelli /* End of L2, byte offset 14, src IP[16:31] */ 507318166cSFlorian Fainelli CFG_UDF_EOL2 | 7, 517318166cSFlorian Fainelli /* End of L2, byte offset 16, dst IP[0:15] */ 527318166cSFlorian Fainelli CFG_UDF_EOL2 | 8, 537318166cSFlorian Fainelli /* End of L2, byte offset 18, dst IP[16:31] */ 547318166cSFlorian Fainelli CFG_UDF_EOL2 | 9, 557318166cSFlorian Fainelli /* End of L3, byte offset 0, src port */ 567318166cSFlorian Fainelli CFG_UDF_EOL3 | 0, 577318166cSFlorian Fainelli /* End of L3, byte offset 2, dst port */ 587318166cSFlorian Fainelli CFG_UDF_EOL3 | 1, 597318166cSFlorian Fainelli 0, 0, 0 607318166cSFlorian Fainelli }, 617318166cSFlorian Fainelli .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, 625d80bcbbSFlorian Fainelli .base_offset = CORE_UDF_0_A_0_8_PORT_0 + UDF_SLICE_OFFSET, 635d80bcbbSFlorian Fainelli }, 645d80bcbbSFlorian Fainelli }, 657318166cSFlorian Fainelli }; 667318166cSFlorian Fainelli 67ba0696c2SFlorian Fainelli /* UDF slices layout for a TCPv6/UDPv6 specification */ 68ba0696c2SFlorian Fainelli static const struct cfp_udf_layout udf_tcpip6_layout = { 69ba0696c2SFlorian Fainelli .udfs = { 70ba0696c2SFlorian Fainelli [0] = { 71ba0696c2SFlorian Fainelli .slices = { 72ba0696c2SFlorian Fainelli /* End of L2, byte offset 8, src IP[0:15] */ 73ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 4, 74ba0696c2SFlorian Fainelli /* End of L2, byte offset 10, src IP[16:31] */ 75ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 5, 76ba0696c2SFlorian Fainelli /* End of L2, byte offset 12, src IP[32:47] */ 77ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 6, 78ba0696c2SFlorian Fainelli /* End of L2, byte offset 14, src IP[48:63] */ 79ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 7, 80ba0696c2SFlorian Fainelli /* End of L2, byte offset 16, src IP[64:79] */ 81ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 8, 82ba0696c2SFlorian Fainelli /* End of L2, byte offset 18, src IP[80:95] */ 83ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 9, 84ba0696c2SFlorian Fainelli /* End of L2, byte offset 20, src IP[96:111] */ 85ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 10, 86ba0696c2SFlorian Fainelli /* End of L2, byte offset 22, src IP[112:127] */ 87ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 11, 88ba0696c2SFlorian Fainelli /* End of L3, byte offset 0, src port */ 89ba0696c2SFlorian Fainelli CFG_UDF_EOL3 | 0, 90ba0696c2SFlorian Fainelli }, 91ba0696c2SFlorian Fainelli .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, 92ba0696c2SFlorian Fainelli .base_offset = CORE_UDF_0_B_0_8_PORT_0, 93ba0696c2SFlorian Fainelli }, 94ba0696c2SFlorian Fainelli [3] = { 95ba0696c2SFlorian Fainelli .slices = { 96ba0696c2SFlorian Fainelli /* End of L2, byte offset 24, dst IP[0:15] */ 97ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 12, 98ba0696c2SFlorian Fainelli /* End of L2, byte offset 26, dst IP[16:31] */ 99ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 13, 100ba0696c2SFlorian Fainelli /* End of L2, byte offset 28, dst IP[32:47] */ 101ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 14, 102ba0696c2SFlorian Fainelli /* End of L2, byte offset 30, dst IP[48:63] */ 103ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 15, 104ba0696c2SFlorian Fainelli /* End of L2, byte offset 32, dst IP[64:79] */ 105ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 16, 106ba0696c2SFlorian Fainelli /* End of L2, byte offset 34, dst IP[80:95] */ 107ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 17, 108ba0696c2SFlorian Fainelli /* End of L2, byte offset 36, dst IP[96:111] */ 109ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 18, 110ba0696c2SFlorian Fainelli /* End of L2, byte offset 38, dst IP[112:127] */ 111ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 19, 112ba0696c2SFlorian Fainelli /* End of L3, byte offset 2, dst port */ 113ba0696c2SFlorian Fainelli CFG_UDF_EOL3 | 1, 114ba0696c2SFlorian Fainelli }, 115ba0696c2SFlorian Fainelli .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, 116ba0696c2SFlorian Fainelli .base_offset = CORE_UDF_0_D_0_11_PORT_0, 117ba0696c2SFlorian Fainelli }, 118ba0696c2SFlorian Fainelli }, 119ba0696c2SFlorian Fainelli }; 120ba0696c2SFlorian Fainelli 1217318166cSFlorian Fainelli static inline unsigned int bcm_sf2_get_num_udf_slices(const u8 *layout) 1227318166cSFlorian Fainelli { 1237318166cSFlorian Fainelli unsigned int i, count = 0; 1247318166cSFlorian Fainelli 1255d80bcbbSFlorian Fainelli for (i = 0; i < UDFS_PER_SLICE; i++) { 1267318166cSFlorian Fainelli if (layout[i] != 0) 1277318166cSFlorian Fainelli count++; 1287318166cSFlorian Fainelli } 1297318166cSFlorian Fainelli 1307318166cSFlorian Fainelli return count; 1317318166cSFlorian Fainelli } 1327318166cSFlorian Fainelli 1335d80bcbbSFlorian Fainelli static inline u32 udf_upper_bits(unsigned int num_udf) 1347318166cSFlorian Fainelli { 1355d80bcbbSFlorian Fainelli return GENMASK(num_udf - 1, 0) >> (UDFS_PER_SLICE - 1); 1365d80bcbbSFlorian Fainelli } 1375d80bcbbSFlorian Fainelli 1385d80bcbbSFlorian Fainelli static inline u32 udf_lower_bits(unsigned int num_udf) 1395d80bcbbSFlorian Fainelli { 1405d80bcbbSFlorian Fainelli return (u8)GENMASK(num_udf - 1, 0); 1415d80bcbbSFlorian Fainelli } 1425d80bcbbSFlorian Fainelli 1435d80bcbbSFlorian Fainelli static unsigned int bcm_sf2_get_slice_number(const struct cfp_udf_layout *l, 1445d80bcbbSFlorian Fainelli unsigned int start) 1455d80bcbbSFlorian Fainelli { 1465d80bcbbSFlorian Fainelli const struct cfp_udf_slice_layout *slice_layout; 1475d80bcbbSFlorian Fainelli unsigned int slice_idx; 1485d80bcbbSFlorian Fainelli 1495d80bcbbSFlorian Fainelli for (slice_idx = start; slice_idx < UDF_NUM_SLICES; slice_idx++) { 1505d80bcbbSFlorian Fainelli slice_layout = &l->udfs[slice_idx]; 1515d80bcbbSFlorian Fainelli if (memcmp(slice_layout->slices, zero_slice, 1525d80bcbbSFlorian Fainelli sizeof(zero_slice))) 1535d80bcbbSFlorian Fainelli break; 1545d80bcbbSFlorian Fainelli } 1555d80bcbbSFlorian Fainelli 1565d80bcbbSFlorian Fainelli return slice_idx; 1575d80bcbbSFlorian Fainelli } 1585d80bcbbSFlorian Fainelli 1595d80bcbbSFlorian Fainelli static void bcm_sf2_cfp_udf_set(struct bcm_sf2_priv *priv, 1605d80bcbbSFlorian Fainelli const struct cfp_udf_layout *layout, 1615d80bcbbSFlorian Fainelli unsigned int slice_num) 1625d80bcbbSFlorian Fainelli { 1635d80bcbbSFlorian Fainelli u32 offset = layout->udfs[slice_num].base_offset; 1647318166cSFlorian Fainelli unsigned int i; 1657318166cSFlorian Fainelli 1665d80bcbbSFlorian Fainelli for (i = 0; i < UDFS_PER_SLICE; i++) 1675d80bcbbSFlorian Fainelli core_writel(priv, layout->udfs[slice_num].slices[i], 1685d80bcbbSFlorian Fainelli offset + i * 4); 1697318166cSFlorian Fainelli } 1707318166cSFlorian Fainelli 1717318166cSFlorian Fainelli static int bcm_sf2_cfp_op(struct bcm_sf2_priv *priv, unsigned int op) 1727318166cSFlorian Fainelli { 1737318166cSFlorian Fainelli unsigned int timeout = 1000; 1747318166cSFlorian Fainelli u32 reg; 1757318166cSFlorian Fainelli 1767318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 1777318166cSFlorian Fainelli reg &= ~(OP_SEL_MASK | RAM_SEL_MASK); 1787318166cSFlorian Fainelli reg |= OP_STR_DONE | op; 1797318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_ACC); 1807318166cSFlorian Fainelli 1817318166cSFlorian Fainelli do { 1827318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 1837318166cSFlorian Fainelli if (!(reg & OP_STR_DONE)) 1847318166cSFlorian Fainelli break; 1857318166cSFlorian Fainelli 1867318166cSFlorian Fainelli cpu_relax(); 1877318166cSFlorian Fainelli } while (timeout--); 1887318166cSFlorian Fainelli 1897318166cSFlorian Fainelli if (!timeout) 1907318166cSFlorian Fainelli return -ETIMEDOUT; 1917318166cSFlorian Fainelli 1927318166cSFlorian Fainelli return 0; 1937318166cSFlorian Fainelli } 1947318166cSFlorian Fainelli 1957318166cSFlorian Fainelli static inline void bcm_sf2_cfp_rule_addr_set(struct bcm_sf2_priv *priv, 1967318166cSFlorian Fainelli unsigned int addr) 1977318166cSFlorian Fainelli { 1987318166cSFlorian Fainelli u32 reg; 1997318166cSFlorian Fainelli 200df191632SFlorian Fainelli WARN_ON(addr >= priv->num_cfp_rules); 2017318166cSFlorian Fainelli 2027318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 2037318166cSFlorian Fainelli reg &= ~(XCESS_ADDR_MASK << XCESS_ADDR_SHIFT); 2047318166cSFlorian Fainelli reg |= addr << XCESS_ADDR_SHIFT; 2057318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_ACC); 2067318166cSFlorian Fainelli } 2077318166cSFlorian Fainelli 2087318166cSFlorian Fainelli static inline unsigned int bcm_sf2_cfp_rule_size(struct bcm_sf2_priv *priv) 2097318166cSFlorian Fainelli { 2107318166cSFlorian Fainelli /* Entry #0 is reserved */ 211df191632SFlorian Fainelli return priv->num_cfp_rules - 1; 2127318166cSFlorian Fainelli } 2137318166cSFlorian Fainelli 21433061458SFlorian Fainelli static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv, 21533061458SFlorian Fainelli unsigned int rule_index, 21633061458SFlorian Fainelli unsigned int port_num, 217ba0696c2SFlorian Fainelli unsigned int queue_num, 218ba0696c2SFlorian Fainelli bool fwd_map_change) 21933061458SFlorian Fainelli { 22033061458SFlorian Fainelli int ret; 22133061458SFlorian Fainelli u32 reg; 22233061458SFlorian Fainelli 22333061458SFlorian Fainelli /* Replace ARL derived destination with DST_MAP derived, define 22433061458SFlorian Fainelli * which port and queue this should be forwarded to. 22533061458SFlorian Fainelli */ 226ba0696c2SFlorian Fainelli if (fwd_map_change) 227ba0696c2SFlorian Fainelli reg = CHANGE_FWRD_MAP_IB_REP_ARL | 228ba0696c2SFlorian Fainelli BIT(port_num + DST_MAP_IB_SHIFT) | 22933061458SFlorian Fainelli CHANGE_TC | queue_num << NEW_TC_SHIFT; 230ba0696c2SFlorian Fainelli else 231ba0696c2SFlorian Fainelli reg = 0; 23233061458SFlorian Fainelli 23333061458SFlorian Fainelli core_writel(priv, reg, CORE_ACT_POL_DATA0); 23433061458SFlorian Fainelli 23533061458SFlorian Fainelli /* Set classification ID that needs to be put in Broadcom tag */ 236ba0696c2SFlorian Fainelli core_writel(priv, rule_index << CHAIN_ID_SHIFT, CORE_ACT_POL_DATA1); 23733061458SFlorian Fainelli 23833061458SFlorian Fainelli core_writel(priv, 0, CORE_ACT_POL_DATA2); 23933061458SFlorian Fainelli 24033061458SFlorian Fainelli /* Configure policer RAM now */ 24133061458SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | ACT_POL_RAM); 24233061458SFlorian Fainelli if (ret) { 24333061458SFlorian Fainelli pr_err("Policer entry at %d failed\n", rule_index); 24433061458SFlorian Fainelli return ret; 24533061458SFlorian Fainelli } 24633061458SFlorian Fainelli 24733061458SFlorian Fainelli /* Disable the policer */ 24833061458SFlorian Fainelli core_writel(priv, POLICER_MODE_DISABLE, CORE_RATE_METER0); 24933061458SFlorian Fainelli 25033061458SFlorian Fainelli /* Now the rate meter */ 25133061458SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | RATE_METER_RAM); 25233061458SFlorian Fainelli if (ret) { 25333061458SFlorian Fainelli pr_err("Meter entry at %d failed\n", rule_index); 25433061458SFlorian Fainelli return ret; 25533061458SFlorian Fainelli } 25633061458SFlorian Fainelli 25733061458SFlorian Fainelli return 0; 25833061458SFlorian Fainelli } 25933061458SFlorian Fainelli 260bc3fc44cSFlorian Fainelli static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv, 261e4f7ef54SPablo Neira Ayuso struct flow_dissector_key_ipv4_addrs *addrs, 262e4f7ef54SPablo Neira Ayuso struct flow_dissector_key_ports *ports, 263bc3fc44cSFlorian Fainelli unsigned int slice_num, 264bc3fc44cSFlorian Fainelli bool mask) 265bc3fc44cSFlorian Fainelli { 266bc3fc44cSFlorian Fainelli u32 reg, offset; 267bc3fc44cSFlorian Fainelli 268bc3fc44cSFlorian Fainelli /* C-Tag [31:24] 269bc3fc44cSFlorian Fainelli * UDF_n_A8 [23:8] 270bc3fc44cSFlorian Fainelli * UDF_n_A7 [7:0] 271bc3fc44cSFlorian Fainelli */ 272bc3fc44cSFlorian Fainelli reg = 0; 273bc3fc44cSFlorian Fainelli if (mask) 274bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(4); 275bc3fc44cSFlorian Fainelli else 276bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(4); 277bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 278bc3fc44cSFlorian Fainelli 279bc3fc44cSFlorian Fainelli /* UDF_n_A7 [31:24] 280bc3fc44cSFlorian Fainelli * UDF_n_A6 [23:8] 281bc3fc44cSFlorian Fainelli * UDF_n_A5 [7:0] 282bc3fc44cSFlorian Fainelli */ 283e4f7ef54SPablo Neira Ayuso reg = be16_to_cpu(ports->dst) >> 8; 284bc3fc44cSFlorian Fainelli if (mask) 285bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(3); 286bc3fc44cSFlorian Fainelli else 287bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(3); 288bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 289bc3fc44cSFlorian Fainelli 290bc3fc44cSFlorian Fainelli /* UDF_n_A5 [31:24] 291bc3fc44cSFlorian Fainelli * UDF_n_A4 [23:8] 292bc3fc44cSFlorian Fainelli * UDF_n_A3 [7:0] 293bc3fc44cSFlorian Fainelli */ 294e4f7ef54SPablo Neira Ayuso reg = (be16_to_cpu(ports->dst) & 0xff) << 24 | 295e4f7ef54SPablo Neira Ayuso (u32)be16_to_cpu(ports->src) << 8 | 296e4f7ef54SPablo Neira Ayuso (be32_to_cpu(addrs->dst) & 0x0000ff00) >> 8; 297bc3fc44cSFlorian Fainelli if (mask) 298bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(2); 299bc3fc44cSFlorian Fainelli else 300bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(2); 301bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 302bc3fc44cSFlorian Fainelli 303bc3fc44cSFlorian Fainelli /* UDF_n_A3 [31:24] 304bc3fc44cSFlorian Fainelli * UDF_n_A2 [23:8] 305bc3fc44cSFlorian Fainelli * UDF_n_A1 [7:0] 306bc3fc44cSFlorian Fainelli */ 307e4f7ef54SPablo Neira Ayuso reg = (u32)(be32_to_cpu(addrs->dst) & 0xff) << 24 | 308e4f7ef54SPablo Neira Ayuso (u32)(be32_to_cpu(addrs->dst) >> 16) << 8 | 309e4f7ef54SPablo Neira Ayuso (be32_to_cpu(addrs->src) & 0x0000ff00) >> 8; 310bc3fc44cSFlorian Fainelli if (mask) 311bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(1); 312bc3fc44cSFlorian Fainelli else 313bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(1); 314bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 315bc3fc44cSFlorian Fainelli 316bc3fc44cSFlorian Fainelli /* UDF_n_A1 [31:24] 317bc3fc44cSFlorian Fainelli * UDF_n_A0 [23:8] 318bc3fc44cSFlorian Fainelli * Reserved [7:4] 319bc3fc44cSFlorian Fainelli * Slice ID [3:2] 320bc3fc44cSFlorian Fainelli * Slice valid [1:0] 321bc3fc44cSFlorian Fainelli */ 322e4f7ef54SPablo Neira Ayuso reg = (u32)(be32_to_cpu(addrs->src) & 0xff) << 24 | 323e4f7ef54SPablo Neira Ayuso (u32)(be32_to_cpu(addrs->src) >> 16) << 8 | 324bc3fc44cSFlorian Fainelli SLICE_NUM(slice_num) | SLICE_VALID; 325bc3fc44cSFlorian Fainelli if (mask) 326bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(0); 327bc3fc44cSFlorian Fainelli else 328bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(0); 329bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 330bc3fc44cSFlorian Fainelli } 331bc3fc44cSFlorian Fainelli 33233061458SFlorian Fainelli static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port, 33333061458SFlorian Fainelli unsigned int port_num, 33433061458SFlorian Fainelli unsigned int queue_num, 3357318166cSFlorian Fainelli struct ethtool_rx_flow_spec *fs) 3367318166cSFlorian Fainelli { 337e4f7ef54SPablo Neira Ayuso struct ethtool_rx_flow_spec_input input = {}; 3387318166cSFlorian Fainelli const struct cfp_udf_layout *layout; 3397318166cSFlorian Fainelli unsigned int slice_num, rule_index; 340e4f7ef54SPablo Neira Ayuso struct ethtool_rx_flow_rule *flow; 341e4f7ef54SPablo Neira Ayuso struct flow_match_ipv4_addrs ipv4; 342e4f7ef54SPablo Neira Ayuso struct flow_match_ports ports; 343e4f7ef54SPablo Neira Ayuso struct flow_match_ip ip; 3447318166cSFlorian Fainelli u8 ip_proto, ip_frag; 3457318166cSFlorian Fainelli u8 num_udf; 3467318166cSFlorian Fainelli u32 reg; 3477318166cSFlorian Fainelli int ret; 3487318166cSFlorian Fainelli 3497318166cSFlorian Fainelli switch (fs->flow_type & ~FLOW_EXT) { 3507318166cSFlorian Fainelli case TCP_V4_FLOW: 3517318166cSFlorian Fainelli ip_proto = IPPROTO_TCP; 3527318166cSFlorian Fainelli break; 3537318166cSFlorian Fainelli case UDP_V4_FLOW: 3547318166cSFlorian Fainelli ip_proto = IPPROTO_UDP; 3557318166cSFlorian Fainelli break; 3567318166cSFlorian Fainelli default: 3577318166cSFlorian Fainelli return -EINVAL; 3587318166cSFlorian Fainelli } 3597318166cSFlorian Fainelli 36033061458SFlorian Fainelli ip_frag = be32_to_cpu(fs->m_ext.data[0]); 36133061458SFlorian Fainelli 36233061458SFlorian Fainelli /* Locate the first rule available */ 36333061458SFlorian Fainelli if (fs->location == RX_CLS_LOC_ANY) 36433061458SFlorian Fainelli rule_index = find_first_zero_bit(priv->cfp.used, 36543a5e00fSFlorian Fainelli priv->num_cfp_rules); 36633061458SFlorian Fainelli else 36733061458SFlorian Fainelli rule_index = fs->location; 36833061458SFlorian Fainelli 36943a5e00fSFlorian Fainelli if (rule_index > bcm_sf2_cfp_rule_size(priv)) 37043a5e00fSFlorian Fainelli return -ENOSPC; 37143a5e00fSFlorian Fainelli 372e4f7ef54SPablo Neira Ayuso input.fs = fs; 373e4f7ef54SPablo Neira Ayuso flow = ethtool_rx_flow_rule_create(&input); 374e4f7ef54SPablo Neira Ayuso if (IS_ERR(flow)) 375e4f7ef54SPablo Neira Ayuso return PTR_ERR(flow); 376e4f7ef54SPablo Neira Ayuso 377e4f7ef54SPablo Neira Ayuso flow_rule_match_ipv4_addrs(flow->rule, &ipv4); 378e4f7ef54SPablo Neira Ayuso flow_rule_match_ports(flow->rule, &ports); 379e4f7ef54SPablo Neira Ayuso flow_rule_match_ip(flow->rule, &ip); 380e4f7ef54SPablo Neira Ayuso 3817318166cSFlorian Fainelli layout = &udf_tcpip4_layout; 3825d80bcbbSFlorian Fainelli /* We only use one UDF slice for now */ 3835d80bcbbSFlorian Fainelli slice_num = bcm_sf2_get_slice_number(layout, 0); 384e4f7ef54SPablo Neira Ayuso if (slice_num == UDF_NUM_SLICES) { 385e4f7ef54SPablo Neira Ayuso ret = -EINVAL; 386e4f7ef54SPablo Neira Ayuso goto out_err_flow_rule; 387e4f7ef54SPablo Neira Ayuso } 3885d80bcbbSFlorian Fainelli 3895d80bcbbSFlorian Fainelli num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); 3907318166cSFlorian Fainelli 3917318166cSFlorian Fainelli /* Apply the UDF layout for this filter */ 3925d80bcbbSFlorian Fainelli bcm_sf2_cfp_udf_set(priv, layout, slice_num); 3937318166cSFlorian Fainelli 3947318166cSFlorian Fainelli /* Apply to all packets received through this port */ 3957318166cSFlorian Fainelli core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7)); 3967318166cSFlorian Fainelli 39733061458SFlorian Fainelli /* Source port map match */ 39833061458SFlorian Fainelli core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7)); 39933061458SFlorian Fainelli 4007318166cSFlorian Fainelli /* S-Tag status [31:30] 4017318166cSFlorian Fainelli * C-Tag status [29:28] 4027318166cSFlorian Fainelli * L2 framing [27:26] 4037318166cSFlorian Fainelli * L3 framing [25:24] 4047318166cSFlorian Fainelli * IP ToS [23:16] 4057318166cSFlorian Fainelli * IP proto [15:08] 4067318166cSFlorian Fainelli * IP Fragm [7] 4077318166cSFlorian Fainelli * Non 1st frag [6] 4087318166cSFlorian Fainelli * IP Authen [5] 4097318166cSFlorian Fainelli * TTL range [4:3] 4107318166cSFlorian Fainelli * PPPoE session [2] 4117318166cSFlorian Fainelli * Reserved [1] 4127318166cSFlorian Fainelli * UDF_Valid[8] [0] 4137318166cSFlorian Fainelli */ 414e4f7ef54SPablo Neira Ayuso core_writel(priv, ip.key->tos << IPTOS_SHIFT | 4155d80bcbbSFlorian Fainelli ip_proto << IPPROTO_SHIFT | ip_frag << IP_FRAG_SHIFT | 4165d80bcbbSFlorian Fainelli udf_upper_bits(num_udf), 4177318166cSFlorian Fainelli CORE_CFP_DATA_PORT(6)); 4187318166cSFlorian Fainelli 419bc3fc44cSFlorian Fainelli /* Mask with the specific layout for IPv4 packets */ 420bc3fc44cSFlorian Fainelli core_writel(priv, layout->udfs[slice_num].mask_value | 421bc3fc44cSFlorian Fainelli udf_upper_bits(num_udf), CORE_CFP_MASK_PORT(6)); 422bc3fc44cSFlorian Fainelli 4237318166cSFlorian Fainelli /* UDF_Valid[7:0] [31:24] 4247318166cSFlorian Fainelli * S-Tag [23:8] 4257318166cSFlorian Fainelli * C-Tag [7:0] 4267318166cSFlorian Fainelli */ 4275d80bcbbSFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); 4287318166cSFlorian Fainelli 4297318166cSFlorian Fainelli /* Mask all but valid UDFs */ 4305d80bcbbSFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); 4317318166cSFlorian Fainelli 432bc3fc44cSFlorian Fainelli /* Program the match and the mask */ 433e4f7ef54SPablo Neira Ayuso bcm_sf2_cfp_slice_ipv4(priv, ipv4.key, ports.key, slice_num, false); 434e4f7ef54SPablo Neira Ayuso bcm_sf2_cfp_slice_ipv4(priv, ipv4.mask, ports.mask, SLICE_NUM_MASK, true); 4357318166cSFlorian Fainelli 4367318166cSFlorian Fainelli /* Insert into TCAM now */ 4377318166cSFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, rule_index); 4387318166cSFlorian Fainelli 4397318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 4407318166cSFlorian Fainelli if (ret) { 4417318166cSFlorian Fainelli pr_err("TCAM entry at addr %d failed\n", rule_index); 442e4f7ef54SPablo Neira Ayuso goto out_err_flow_rule; 4437318166cSFlorian Fainelli } 4447318166cSFlorian Fainelli 44533061458SFlorian Fainelli /* Insert into Action and policer RAMs now */ 446ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_act_pol_set(priv, rule_index, port_num, 447ba0696c2SFlorian Fainelli queue_num, true); 44833061458SFlorian Fainelli if (ret) 449e4f7ef54SPablo Neira Ayuso goto out_err_flow_rule; 4507318166cSFlorian Fainelli 4517318166cSFlorian Fainelli /* Turn on CFP for this rule now */ 4527318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_CTL_REG); 4537318166cSFlorian Fainelli reg |= BIT(port); 4547318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_CTL_REG); 4557318166cSFlorian Fainelli 4567318166cSFlorian Fainelli /* Flag the rule as being used and return it */ 4577318166cSFlorian Fainelli set_bit(rule_index, priv->cfp.used); 458ba0696c2SFlorian Fainelli set_bit(rule_index, priv->cfp.unique); 4597318166cSFlorian Fainelli fs->location = rule_index; 4607318166cSFlorian Fainelli 4617318166cSFlorian Fainelli return 0; 462e4f7ef54SPablo Neira Ayuso 463e4f7ef54SPablo Neira Ayuso out_err_flow_rule: 464e4f7ef54SPablo Neira Ayuso ethtool_rx_flow_rule_destroy(flow); 465e4f7ef54SPablo Neira Ayuso return ret; 4667318166cSFlorian Fainelli } 4677318166cSFlorian Fainelli 468ba0696c2SFlorian Fainelli static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv, 469ba0696c2SFlorian Fainelli const __be32 *ip6_addr, const __be16 port, 470dd8eff68SFlorian Fainelli unsigned int slice_num, 471dd8eff68SFlorian Fainelli bool mask) 472ba0696c2SFlorian Fainelli { 473dd8eff68SFlorian Fainelli u32 reg, tmp, val, offset; 474ba0696c2SFlorian Fainelli 475ba0696c2SFlorian Fainelli /* C-Tag [31:24] 476ba0696c2SFlorian Fainelli * UDF_n_B8 [23:8] (port) 477ba0696c2SFlorian Fainelli * UDF_n_B7 (upper) [7:0] (addr[15:8]) 478ba0696c2SFlorian Fainelli */ 479ba0696c2SFlorian Fainelli reg = be32_to_cpu(ip6_addr[3]); 480ba0696c2SFlorian Fainelli val = (u32)be16_to_cpu(port) << 8 | ((reg >> 8) & 0xff); 481dd8eff68SFlorian Fainelli if (mask) 482dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(4); 483dd8eff68SFlorian Fainelli else 484dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(4); 485dd8eff68SFlorian Fainelli core_writel(priv, val, offset); 486ba0696c2SFlorian Fainelli 487ba0696c2SFlorian Fainelli /* UDF_n_B7 (lower) [31:24] (addr[7:0]) 488ba0696c2SFlorian Fainelli * UDF_n_B6 [23:8] (addr[31:16]) 489ba0696c2SFlorian Fainelli * UDF_n_B5 (upper) [7:0] (addr[47:40]) 490ba0696c2SFlorian Fainelli */ 491ba0696c2SFlorian Fainelli tmp = be32_to_cpu(ip6_addr[2]); 492ba0696c2SFlorian Fainelli val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 | 493ba0696c2SFlorian Fainelli ((tmp >> 8) & 0xff); 494dd8eff68SFlorian Fainelli if (mask) 495dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(3); 496dd8eff68SFlorian Fainelli else 497dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(3); 498dd8eff68SFlorian Fainelli core_writel(priv, val, offset); 499ba0696c2SFlorian Fainelli 500ba0696c2SFlorian Fainelli /* UDF_n_B5 (lower) [31:24] (addr[39:32]) 501ba0696c2SFlorian Fainelli * UDF_n_B4 [23:8] (addr[63:48]) 502ba0696c2SFlorian Fainelli * UDF_n_B3 (upper) [7:0] (addr[79:72]) 503ba0696c2SFlorian Fainelli */ 504ba0696c2SFlorian Fainelli reg = be32_to_cpu(ip6_addr[1]); 505ba0696c2SFlorian Fainelli val = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 | 506ba0696c2SFlorian Fainelli ((reg >> 8) & 0xff); 507dd8eff68SFlorian Fainelli if (mask) 508dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(2); 509dd8eff68SFlorian Fainelli else 510dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(2); 511dd8eff68SFlorian Fainelli core_writel(priv, val, offset); 512ba0696c2SFlorian Fainelli 513ba0696c2SFlorian Fainelli /* UDF_n_B3 (lower) [31:24] (addr[71:64]) 514ba0696c2SFlorian Fainelli * UDF_n_B2 [23:8] (addr[95:80]) 515ba0696c2SFlorian Fainelli * UDF_n_B1 (upper) [7:0] (addr[111:104]) 516ba0696c2SFlorian Fainelli */ 517ba0696c2SFlorian Fainelli tmp = be32_to_cpu(ip6_addr[0]); 518ba0696c2SFlorian Fainelli val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 | 519ba0696c2SFlorian Fainelli ((tmp >> 8) & 0xff); 520dd8eff68SFlorian Fainelli if (mask) 521dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(1); 522dd8eff68SFlorian Fainelli else 523dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(1); 524dd8eff68SFlorian Fainelli core_writel(priv, val, offset); 525ba0696c2SFlorian Fainelli 526ba0696c2SFlorian Fainelli /* UDF_n_B1 (lower) [31:24] (addr[103:96]) 527ba0696c2SFlorian Fainelli * UDF_n_B0 [23:8] (addr[127:112]) 528ba0696c2SFlorian Fainelli * Reserved [7:4] 529ba0696c2SFlorian Fainelli * Slice ID [3:2] 530ba0696c2SFlorian Fainelli * Slice valid [1:0] 531ba0696c2SFlorian Fainelli */ 532ba0696c2SFlorian Fainelli reg = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 | 533ba0696c2SFlorian Fainelli SLICE_NUM(slice_num) | SLICE_VALID; 534dd8eff68SFlorian Fainelli if (mask) 535dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(0); 536dd8eff68SFlorian Fainelli else 537dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(0); 538dd8eff68SFlorian Fainelli core_writel(priv, reg, offset); 539ba0696c2SFlorian Fainelli } 540ba0696c2SFlorian Fainelli 541ae7a5affSFlorian Fainelli static struct cfp_rule *bcm_sf2_cfp_rule_find(struct bcm_sf2_priv *priv, 542ae7a5affSFlorian Fainelli int port, u32 location) 543ae7a5affSFlorian Fainelli { 544ae7a5affSFlorian Fainelli struct cfp_rule *rule = NULL; 545ae7a5affSFlorian Fainelli 546ae7a5affSFlorian Fainelli list_for_each_entry(rule, &priv->cfp.rules_list, next) { 547ae7a5affSFlorian Fainelli if (rule->port == port && rule->fs.location == location) 548ae7a5affSFlorian Fainelli break; 549f9086200Skbuild test robot } 550ae7a5affSFlorian Fainelli 551ae7a5affSFlorian Fainelli return rule; 552ae7a5affSFlorian Fainelli } 553ae7a5affSFlorian Fainelli 554ae7a5affSFlorian Fainelli static int bcm_sf2_cfp_rule_cmp(struct bcm_sf2_priv *priv, int port, 555ae7a5affSFlorian Fainelli struct ethtool_rx_flow_spec *fs) 556ae7a5affSFlorian Fainelli { 557ae7a5affSFlorian Fainelli struct cfp_rule *rule = NULL; 558ae7a5affSFlorian Fainelli size_t fs_size = 0; 559ae7a5affSFlorian Fainelli int ret = 1; 560ae7a5affSFlorian Fainelli 561ae7a5affSFlorian Fainelli if (list_empty(&priv->cfp.rules_list)) 562ae7a5affSFlorian Fainelli return ret; 563ae7a5affSFlorian Fainelli 564ae7a5affSFlorian Fainelli list_for_each_entry(rule, &priv->cfp.rules_list, next) { 565ae7a5affSFlorian Fainelli ret = 1; 566ae7a5affSFlorian Fainelli if (rule->port != port) 567ae7a5affSFlorian Fainelli continue; 568ae7a5affSFlorian Fainelli 569ae7a5affSFlorian Fainelli if (rule->fs.flow_type != fs->flow_type || 570ae7a5affSFlorian Fainelli rule->fs.ring_cookie != fs->ring_cookie || 571ae7a5affSFlorian Fainelli rule->fs.m_ext.data[0] != fs->m_ext.data[0]) 572ae7a5affSFlorian Fainelli continue; 573ae7a5affSFlorian Fainelli 574ae7a5affSFlorian Fainelli switch (fs->flow_type & ~FLOW_EXT) { 575ae7a5affSFlorian Fainelli case TCP_V6_FLOW: 576ae7a5affSFlorian Fainelli case UDP_V6_FLOW: 577ae7a5affSFlorian Fainelli fs_size = sizeof(struct ethtool_tcpip6_spec); 578ae7a5affSFlorian Fainelli break; 579ae7a5affSFlorian Fainelli case TCP_V4_FLOW: 580ae7a5affSFlorian Fainelli case UDP_V4_FLOW: 581ae7a5affSFlorian Fainelli fs_size = sizeof(struct ethtool_tcpip4_spec); 582ae7a5affSFlorian Fainelli break; 583ae7a5affSFlorian Fainelli default: 584ae7a5affSFlorian Fainelli continue; 585ae7a5affSFlorian Fainelli } 586ae7a5affSFlorian Fainelli 587ae7a5affSFlorian Fainelli ret = memcmp(&rule->fs.h_u, &fs->h_u, fs_size); 588ae7a5affSFlorian Fainelli ret |= memcmp(&rule->fs.m_u, &fs->m_u, fs_size); 589ae7a5affSFlorian Fainelli if (ret == 0) 590ae7a5affSFlorian Fainelli break; 591ae7a5affSFlorian Fainelli } 592ae7a5affSFlorian Fainelli 593ae7a5affSFlorian Fainelli return ret; 594ae7a5affSFlorian Fainelli } 595ae7a5affSFlorian Fainelli 596ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, 597ba0696c2SFlorian Fainelli unsigned int port_num, 598ba0696c2SFlorian Fainelli unsigned int queue_num, 599ba0696c2SFlorian Fainelli struct ethtool_rx_flow_spec *fs) 600ba0696c2SFlorian Fainelli { 601dd8eff68SFlorian Fainelli struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec; 602e4f7ef54SPablo Neira Ayuso struct ethtool_rx_flow_spec_input input = {}; 603ba0696c2SFlorian Fainelli unsigned int slice_num, rule_index[2]; 604ba0696c2SFlorian Fainelli const struct cfp_udf_layout *layout; 605e4f7ef54SPablo Neira Ayuso struct ethtool_rx_flow_rule *flow; 606e4f7ef54SPablo Neira Ayuso struct flow_match_ipv6_addrs ipv6; 607e4f7ef54SPablo Neira Ayuso struct flow_match_ports ports; 608ba0696c2SFlorian Fainelli u8 ip_proto, ip_frag; 609ba0696c2SFlorian Fainelli int ret = 0; 610ba0696c2SFlorian Fainelli u8 num_udf; 611ba0696c2SFlorian Fainelli u32 reg; 612ba0696c2SFlorian Fainelli 613ba0696c2SFlorian Fainelli switch (fs->flow_type & ~FLOW_EXT) { 614ba0696c2SFlorian Fainelli case TCP_V6_FLOW: 615ba0696c2SFlorian Fainelli ip_proto = IPPROTO_TCP; 616ba0696c2SFlorian Fainelli v6_spec = &fs->h_u.tcp_ip6_spec; 617dd8eff68SFlorian Fainelli v6_m_spec = &fs->m_u.tcp_ip6_spec; 618ba0696c2SFlorian Fainelli break; 619ba0696c2SFlorian Fainelli case UDP_V6_FLOW: 620ba0696c2SFlorian Fainelli ip_proto = IPPROTO_UDP; 621ba0696c2SFlorian Fainelli v6_spec = &fs->h_u.udp_ip6_spec; 622dd8eff68SFlorian Fainelli v6_m_spec = &fs->m_u.udp_ip6_spec; 623ba0696c2SFlorian Fainelli break; 624ba0696c2SFlorian Fainelli default: 625ba0696c2SFlorian Fainelli return -EINVAL; 626ba0696c2SFlorian Fainelli } 627ba0696c2SFlorian Fainelli 628ba0696c2SFlorian Fainelli ip_frag = be32_to_cpu(fs->m_ext.data[0]); 629ba0696c2SFlorian Fainelli 630ba0696c2SFlorian Fainelli layout = &udf_tcpip6_layout; 631ba0696c2SFlorian Fainelli slice_num = bcm_sf2_get_slice_number(layout, 0); 632ba0696c2SFlorian Fainelli if (slice_num == UDF_NUM_SLICES) 633ba0696c2SFlorian Fainelli return -EINVAL; 634ba0696c2SFlorian Fainelli 635ba0696c2SFlorian Fainelli num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); 636ba0696c2SFlorian Fainelli 637ba0696c2SFlorian Fainelli /* Negotiate two indexes, one for the second half which we are chained 638ba0696c2SFlorian Fainelli * from, which is what we will return to user-space, and a second one 639ba0696c2SFlorian Fainelli * which is used to store its first half. That first half does not 640ba0696c2SFlorian Fainelli * allow any choice of placement, so it just needs to find the next 641ba0696c2SFlorian Fainelli * available bit. We return the second half as fs->location because 642ba0696c2SFlorian Fainelli * that helps with the rule lookup later on since the second half is 643ba0696c2SFlorian Fainelli * chained from its first half, we can easily identify IPv6 CFP rules 644ba0696c2SFlorian Fainelli * by looking whether they carry a CHAIN_ID. 645ba0696c2SFlorian Fainelli * 646ba0696c2SFlorian Fainelli * We also want the second half to have a lower rule_index than its 647ba0696c2SFlorian Fainelli * first half because the HW search is by incrementing addresses. 648ba0696c2SFlorian Fainelli */ 649ba0696c2SFlorian Fainelli if (fs->location == RX_CLS_LOC_ANY) 6506c05561cSFlorian Fainelli rule_index[1] = find_first_zero_bit(priv->cfp.used, 6516c05561cSFlorian Fainelli priv->num_cfp_rules); 652ba0696c2SFlorian Fainelli else 6536c05561cSFlorian Fainelli rule_index[1] = fs->location; 6546c05561cSFlorian Fainelli if (rule_index[1] > bcm_sf2_cfp_rule_size(priv)) 6556c05561cSFlorian Fainelli return -ENOSPC; 656ba0696c2SFlorian Fainelli 657ba0696c2SFlorian Fainelli /* Flag it as used (cleared on error path) such that we can immediately 658ba0696c2SFlorian Fainelli * obtain a second one to chain from. 659ba0696c2SFlorian Fainelli */ 6606c05561cSFlorian Fainelli set_bit(rule_index[1], priv->cfp.used); 661ba0696c2SFlorian Fainelli 6626c05561cSFlorian Fainelli rule_index[0] = find_first_zero_bit(priv->cfp.used, 6636c05561cSFlorian Fainelli priv->num_cfp_rules); 6646c05561cSFlorian Fainelli if (rule_index[0] > bcm_sf2_cfp_rule_size(priv)) { 665ba0696c2SFlorian Fainelli ret = -ENOSPC; 666ba0696c2SFlorian Fainelli goto out_err; 667ba0696c2SFlorian Fainelli } 668ba0696c2SFlorian Fainelli 669e4f7ef54SPablo Neira Ayuso input.fs = fs; 670e4f7ef54SPablo Neira Ayuso flow = ethtool_rx_flow_rule_create(&input); 671e4f7ef54SPablo Neira Ayuso if (IS_ERR(flow)) { 672e4f7ef54SPablo Neira Ayuso ret = PTR_ERR(flow); 673e4f7ef54SPablo Neira Ayuso goto out_err; 674e4f7ef54SPablo Neira Ayuso } 675e4f7ef54SPablo Neira Ayuso flow_rule_match_ipv6_addrs(flow->rule, &ipv6); 676e4f7ef54SPablo Neira Ayuso flow_rule_match_ports(flow->rule, &ports); 677e4f7ef54SPablo Neira Ayuso 678ba0696c2SFlorian Fainelli /* Apply the UDF layout for this filter */ 679ba0696c2SFlorian Fainelli bcm_sf2_cfp_udf_set(priv, layout, slice_num); 680ba0696c2SFlorian Fainelli 681ba0696c2SFlorian Fainelli /* Apply to all packets received through this port */ 682ba0696c2SFlorian Fainelli core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7)); 683ba0696c2SFlorian Fainelli 684ba0696c2SFlorian Fainelli /* Source port map match */ 685ba0696c2SFlorian Fainelli core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7)); 686ba0696c2SFlorian Fainelli 687ba0696c2SFlorian Fainelli /* S-Tag status [31:30] 688ba0696c2SFlorian Fainelli * C-Tag status [29:28] 689ba0696c2SFlorian Fainelli * L2 framing [27:26] 690ba0696c2SFlorian Fainelli * L3 framing [25:24] 691ba0696c2SFlorian Fainelli * IP ToS [23:16] 692ba0696c2SFlorian Fainelli * IP proto [15:08] 693ba0696c2SFlorian Fainelli * IP Fragm [7] 694ba0696c2SFlorian Fainelli * Non 1st frag [6] 695ba0696c2SFlorian Fainelli * IP Authen [5] 696ba0696c2SFlorian Fainelli * TTL range [4:3] 697ba0696c2SFlorian Fainelli * PPPoE session [2] 698ba0696c2SFlorian Fainelli * Reserved [1] 699ba0696c2SFlorian Fainelli * UDF_Valid[8] [0] 700ba0696c2SFlorian Fainelli */ 701ba0696c2SFlorian Fainelli reg = 1 << L3_FRAMING_SHIFT | ip_proto << IPPROTO_SHIFT | 702ba0696c2SFlorian Fainelli ip_frag << IP_FRAG_SHIFT | udf_upper_bits(num_udf); 703ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(6)); 704ba0696c2SFlorian Fainelli 705ba0696c2SFlorian Fainelli /* Mask with the specific layout for IPv6 packets including 706ba0696c2SFlorian Fainelli * UDF_Valid[8] 707ba0696c2SFlorian Fainelli */ 708ba0696c2SFlorian Fainelli reg = layout->udfs[slice_num].mask_value | udf_upper_bits(num_udf); 709ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); 710ba0696c2SFlorian Fainelli 711ba0696c2SFlorian Fainelli /* UDF_Valid[7:0] [31:24] 712ba0696c2SFlorian Fainelli * S-Tag [23:8] 713ba0696c2SFlorian Fainelli * C-Tag [7:0] 714ba0696c2SFlorian Fainelli */ 715ba0696c2SFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); 716ba0696c2SFlorian Fainelli 717ba0696c2SFlorian Fainelli /* Mask all but valid UDFs */ 718ba0696c2SFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); 719ba0696c2SFlorian Fainelli 720ba0696c2SFlorian Fainelli /* Slice the IPv6 source address and port */ 721e4f7ef54SPablo Neira Ayuso bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->src.in6_u.u6_addr32, 722e4f7ef54SPablo Neira Ayuso ports.key->src, slice_num, false); 723e4f7ef54SPablo Neira Ayuso bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->src.in6_u.u6_addr32, 724e4f7ef54SPablo Neira Ayuso ports.mask->src, SLICE_NUM_MASK, true); 725ba0696c2SFlorian Fainelli 726ba0696c2SFlorian Fainelli /* Insert into TCAM now because we need to insert a second rule */ 727ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, rule_index[0]); 728ba0696c2SFlorian Fainelli 729ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 730ba0696c2SFlorian Fainelli if (ret) { 731ba0696c2SFlorian Fainelli pr_err("TCAM entry at addr %d failed\n", rule_index[0]); 732e4f7ef54SPablo Neira Ayuso goto out_err_flow_rule; 733ba0696c2SFlorian Fainelli } 734ba0696c2SFlorian Fainelli 735ba0696c2SFlorian Fainelli /* Insert into Action and policer RAMs now */ 736ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port_num, 737ba0696c2SFlorian Fainelli queue_num, false); 738ba0696c2SFlorian Fainelli if (ret) 739e4f7ef54SPablo Neira Ayuso goto out_err_flow_rule; 740ba0696c2SFlorian Fainelli 741ba0696c2SFlorian Fainelli /* Now deal with the second slice to chain this rule */ 742ba0696c2SFlorian Fainelli slice_num = bcm_sf2_get_slice_number(layout, slice_num + 1); 743ba0696c2SFlorian Fainelli if (slice_num == UDF_NUM_SLICES) { 744ba0696c2SFlorian Fainelli ret = -EINVAL; 745e4f7ef54SPablo Neira Ayuso goto out_err_flow_rule; 746ba0696c2SFlorian Fainelli } 747ba0696c2SFlorian Fainelli 748ba0696c2SFlorian Fainelli num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); 749ba0696c2SFlorian Fainelli 750ba0696c2SFlorian Fainelli /* Apply the UDF layout for this filter */ 751ba0696c2SFlorian Fainelli bcm_sf2_cfp_udf_set(priv, layout, slice_num); 752ba0696c2SFlorian Fainelli 753ba0696c2SFlorian Fainelli /* Chained rule, source port match is coming from the rule we are 754ba0696c2SFlorian Fainelli * chained from. 755ba0696c2SFlorian Fainelli */ 756ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_DATA_PORT(7)); 757ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_MASK_PORT(7)); 758ba0696c2SFlorian Fainelli 759ba0696c2SFlorian Fainelli /* 760ba0696c2SFlorian Fainelli * CHAIN ID [31:24] chain to previous slice 761ba0696c2SFlorian Fainelli * Reserved [23:20] 762ba0696c2SFlorian Fainelli * UDF_Valid[11:8] [19:16] 763ba0696c2SFlorian Fainelli * UDF_Valid[7:0] [15:8] 764ba0696c2SFlorian Fainelli * UDF_n_D11 [7:0] 765ba0696c2SFlorian Fainelli */ 766ba0696c2SFlorian Fainelli reg = rule_index[0] << 24 | udf_upper_bits(num_udf) << 16 | 767ba0696c2SFlorian Fainelli udf_lower_bits(num_udf) << 8; 768ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(6)); 769ba0696c2SFlorian Fainelli 770ba0696c2SFlorian Fainelli /* Mask all except chain ID, UDF Valid[8] and UDF Valid[7:0] */ 771ba0696c2SFlorian Fainelli reg = XCESS_ADDR_MASK << 24 | udf_upper_bits(num_udf) << 16 | 772ba0696c2SFlorian Fainelli udf_lower_bits(num_udf) << 8; 773ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); 774ba0696c2SFlorian Fainelli 775ba0696c2SFlorian Fainelli /* Don't care */ 776ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_DATA_PORT(5)); 777ba0696c2SFlorian Fainelli 778ba0696c2SFlorian Fainelli /* Mask all */ 779ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_MASK_PORT(5)); 780ba0696c2SFlorian Fainelli 781e4f7ef54SPablo Neira Ayuso bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->dst.in6_u.u6_addr32, 782e4f7ef54SPablo Neira Ayuso ports.key->dst, slice_num, false); 783e4f7ef54SPablo Neira Ayuso bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->dst.in6_u.u6_addr32, 784e4f7ef54SPablo Neira Ayuso ports.key->dst, SLICE_NUM_MASK, true); 785ba0696c2SFlorian Fainelli 786ba0696c2SFlorian Fainelli /* Insert into TCAM now */ 787ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, rule_index[1]); 788ba0696c2SFlorian Fainelli 789ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 790ba0696c2SFlorian Fainelli if (ret) { 791ba0696c2SFlorian Fainelli pr_err("TCAM entry at addr %d failed\n", rule_index[1]); 792e4f7ef54SPablo Neira Ayuso goto out_err_flow_rule; 793ba0696c2SFlorian Fainelli } 794ba0696c2SFlorian Fainelli 795ba0696c2SFlorian Fainelli /* Insert into Action and policer RAMs now, set chain ID to 796ba0696c2SFlorian Fainelli * the one we are chained to 797ba0696c2SFlorian Fainelli */ 7986fef90c6SFlorian Fainelli ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[1], port_num, 799ba0696c2SFlorian Fainelli queue_num, true); 800ba0696c2SFlorian Fainelli if (ret) 801e4f7ef54SPablo Neira Ayuso goto out_err_flow_rule; 802ba0696c2SFlorian Fainelli 803ba0696c2SFlorian Fainelli /* Turn on CFP for this rule now */ 804ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_CTL_REG); 805ba0696c2SFlorian Fainelli reg |= BIT(port); 806ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_CTL_REG); 807ba0696c2SFlorian Fainelli 808ba0696c2SFlorian Fainelli /* Flag the second half rule as being used now, return it as the 809ba0696c2SFlorian Fainelli * location, and flag it as unique while dumping rules 810ba0696c2SFlorian Fainelli */ 8116c05561cSFlorian Fainelli set_bit(rule_index[0], priv->cfp.used); 812ba0696c2SFlorian Fainelli set_bit(rule_index[1], priv->cfp.unique); 813ba0696c2SFlorian Fainelli fs->location = rule_index[1]; 814ba0696c2SFlorian Fainelli 815ba0696c2SFlorian Fainelli return ret; 816ba0696c2SFlorian Fainelli 817e4f7ef54SPablo Neira Ayuso out_err_flow_rule: 818e4f7ef54SPablo Neira Ayuso ethtool_rx_flow_rule_destroy(flow); 819ba0696c2SFlorian Fainelli out_err: 8206c05561cSFlorian Fainelli clear_bit(rule_index[1], priv->cfp.used); 821ba0696c2SFlorian Fainelli return ret; 822ba0696c2SFlorian Fainelli } 823ba0696c2SFlorian Fainelli 824ce24b08aSFlorian Fainelli static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port, 82533061458SFlorian Fainelli struct ethtool_rx_flow_spec *fs) 82633061458SFlorian Fainelli { 82733061458SFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 8288a75f4f2SFlorian Fainelli s8 cpu_port = ds->ports[port].cpu_dp->index; 8298a75f4f2SFlorian Fainelli __u64 ring_cookie = fs->ring_cookie; 83033061458SFlorian Fainelli unsigned int queue_num, port_num; 831ce24b08aSFlorian Fainelli int ret; 832ae7a5affSFlorian Fainelli 8338a75f4f2SFlorian Fainelli /* This rule is a Wake-on-LAN filter and we must specifically 8348a75f4f2SFlorian Fainelli * target the CPU port in order for it to be working. 8358a75f4f2SFlorian Fainelli */ 8368a75f4f2SFlorian Fainelli if (ring_cookie == RX_CLS_FLOW_WAKE) 8378a75f4f2SFlorian Fainelli ring_cookie = cpu_port * SF2_NUM_EGRESS_QUEUES; 8388a75f4f2SFlorian Fainelli 83933061458SFlorian Fainelli /* We do not support discarding packets, check that the 84033061458SFlorian Fainelli * destination port is enabled and that we are within the 84133061458SFlorian Fainelli * number of ports supported by the switch 84233061458SFlorian Fainelli */ 8438a75f4f2SFlorian Fainelli port_num = ring_cookie / SF2_NUM_EGRESS_QUEUES; 84433061458SFlorian Fainelli 8458a75f4f2SFlorian Fainelli if (ring_cookie == RX_CLS_FLOW_DISC || 8462104bc0aSFlorian Fainelli !(dsa_is_user_port(ds, port_num) || 8472104bc0aSFlorian Fainelli dsa_is_cpu_port(ds, port_num)) || 84833061458SFlorian Fainelli port_num >= priv->hw_params.num_ports) 84933061458SFlorian Fainelli return -EINVAL; 85033061458SFlorian Fainelli /* 85133061458SFlorian Fainelli * We have a small oddity where Port 6 just does not have a 85233061458SFlorian Fainelli * valid bit here (so we substract by one). 85333061458SFlorian Fainelli */ 8548a75f4f2SFlorian Fainelli queue_num = ring_cookie % SF2_NUM_EGRESS_QUEUES; 85533061458SFlorian Fainelli if (port_num >= 7) 85633061458SFlorian Fainelli port_num -= 1; 85733061458SFlorian Fainelli 858ba0696c2SFlorian Fainelli switch (fs->flow_type & ~FLOW_EXT) { 859ba0696c2SFlorian Fainelli case TCP_V4_FLOW: 860ba0696c2SFlorian Fainelli case UDP_V4_FLOW: 861ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv4_rule_set(priv, port, port_num, 862ba0696c2SFlorian Fainelli queue_num, fs); 863ba0696c2SFlorian Fainelli break; 864ba0696c2SFlorian Fainelli case TCP_V6_FLOW: 865ba0696c2SFlorian Fainelli case UDP_V6_FLOW: 866ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv6_rule_set(priv, port, port_num, 867ba0696c2SFlorian Fainelli queue_num, fs); 868ba0696c2SFlorian Fainelli break; 869ba0696c2SFlorian Fainelli default: 870ae7a5affSFlorian Fainelli ret = -EINVAL; 871ba0696c2SFlorian Fainelli break; 87233061458SFlorian Fainelli } 87333061458SFlorian Fainelli 874ce24b08aSFlorian Fainelli return ret; 875ce24b08aSFlorian Fainelli } 876ce24b08aSFlorian Fainelli 877ce24b08aSFlorian Fainelli static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, 878ce24b08aSFlorian Fainelli struct ethtool_rx_flow_spec *fs) 879ce24b08aSFlorian Fainelli { 880ce24b08aSFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 881ce24b08aSFlorian Fainelli struct cfp_rule *rule = NULL; 882ce24b08aSFlorian Fainelli int ret = -EINVAL; 883ce24b08aSFlorian Fainelli 884ce24b08aSFlorian Fainelli /* Check for unsupported extensions */ 885ce24b08aSFlorian Fainelli if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype || 886ce24b08aSFlorian Fainelli fs->m_ext.data[1])) 887ce24b08aSFlorian Fainelli return -EINVAL; 888ce24b08aSFlorian Fainelli 889ce24b08aSFlorian Fainelli if (fs->location != RX_CLS_LOC_ANY && 890ce24b08aSFlorian Fainelli test_bit(fs->location, priv->cfp.used)) 891ce24b08aSFlorian Fainelli return -EBUSY; 892ce24b08aSFlorian Fainelli 893ce24b08aSFlorian Fainelli if (fs->location != RX_CLS_LOC_ANY && 894ce24b08aSFlorian Fainelli fs->location > bcm_sf2_cfp_rule_size(priv)) 895ce24b08aSFlorian Fainelli return -EINVAL; 896ce24b08aSFlorian Fainelli 897ce24b08aSFlorian Fainelli ret = bcm_sf2_cfp_rule_cmp(priv, port, fs); 898ce24b08aSFlorian Fainelli if (ret == 0) 899ce24b08aSFlorian Fainelli return -EEXIST; 900ce24b08aSFlorian Fainelli 901ce24b08aSFlorian Fainelli rule = kzalloc(sizeof(*rule), GFP_KERNEL); 902ce24b08aSFlorian Fainelli if (!rule) 903ce24b08aSFlorian Fainelli return -ENOMEM; 904ce24b08aSFlorian Fainelli 905ce24b08aSFlorian Fainelli ret = bcm_sf2_cfp_rule_insert(ds, port, fs); 906ae7a5affSFlorian Fainelli if (ret) { 907ae7a5affSFlorian Fainelli kfree(rule); 908ae7a5affSFlorian Fainelli return ret; 909ae7a5affSFlorian Fainelli } 910ae7a5affSFlorian Fainelli 911ae7a5affSFlorian Fainelli rule->port = port; 912ae7a5affSFlorian Fainelli memcpy(&rule->fs, fs, sizeof(*fs)); 913ae7a5affSFlorian Fainelli list_add_tail(&rule->next, &priv->cfp.rules_list); 914ae7a5affSFlorian Fainelli 915ba0696c2SFlorian Fainelli return ret; 916ba0696c2SFlorian Fainelli } 917ba0696c2SFlorian Fainelli 918ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_rule_del_one(struct bcm_sf2_priv *priv, int port, 919ba0696c2SFlorian Fainelli u32 loc, u32 *next_loc) 9207318166cSFlorian Fainelli { 9217318166cSFlorian Fainelli int ret; 9227318166cSFlorian Fainelli u32 reg; 9237318166cSFlorian Fainelli 9247318166cSFlorian Fainelli /* Indicate which rule we want to read */ 9257318166cSFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, loc); 9267318166cSFlorian Fainelli 9277318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); 9287318166cSFlorian Fainelli if (ret) 9297318166cSFlorian Fainelli return ret; 9307318166cSFlorian Fainelli 931ba0696c2SFlorian Fainelli /* Check if this is possibly an IPv6 rule that would 932ba0696c2SFlorian Fainelli * indicate we need to delete its companion rule 933ba0696c2SFlorian Fainelli * as well 934ba0696c2SFlorian Fainelli */ 935ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); 936ba0696c2SFlorian Fainelli if (next_loc) 937ba0696c2SFlorian Fainelli *next_loc = (reg >> 24) & CHAIN_ID_MASK; 938ba0696c2SFlorian Fainelli 9397318166cSFlorian Fainelli /* Clear its valid bits */ 9407318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(0)); 9417318166cSFlorian Fainelli reg &= ~SLICE_VALID; 9427318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(0)); 9437318166cSFlorian Fainelli 9447318166cSFlorian Fainelli /* Write back this entry into the TCAM now */ 9457318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 9467318166cSFlorian Fainelli if (ret) 9477318166cSFlorian Fainelli return ret; 9487318166cSFlorian Fainelli 9497318166cSFlorian Fainelli clear_bit(loc, priv->cfp.used); 950ba0696c2SFlorian Fainelli clear_bit(loc, priv->cfp.unique); 9517318166cSFlorian Fainelli 9527318166cSFlorian Fainelli return 0; 9537318166cSFlorian Fainelli } 9547318166cSFlorian Fainelli 955ce24b08aSFlorian Fainelli static int bcm_sf2_cfp_rule_remove(struct bcm_sf2_priv *priv, int port, 956ba0696c2SFlorian Fainelli u32 loc) 957ba0696c2SFlorian Fainelli { 958ba0696c2SFlorian Fainelli u32 next_loc = 0; 959ba0696c2SFlorian Fainelli int ret; 960ba0696c2SFlorian Fainelli 961ce24b08aSFlorian Fainelli ret = bcm_sf2_cfp_rule_del_one(priv, port, loc, &next_loc); 962ce24b08aSFlorian Fainelli if (ret) 963ce24b08aSFlorian Fainelli return ret; 964ce24b08aSFlorian Fainelli 965ce24b08aSFlorian Fainelli /* If this was an IPv6 rule, delete is companion rule too */ 966ce24b08aSFlorian Fainelli if (next_loc) 967ce24b08aSFlorian Fainelli ret = bcm_sf2_cfp_rule_del_one(priv, port, next_loc, NULL); 968ce24b08aSFlorian Fainelli 969ce24b08aSFlorian Fainelli return ret; 970ce24b08aSFlorian Fainelli } 971ce24b08aSFlorian Fainelli 972ce24b08aSFlorian Fainelli static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, u32 loc) 973ce24b08aSFlorian Fainelli { 974ce24b08aSFlorian Fainelli struct cfp_rule *rule; 975ce24b08aSFlorian Fainelli int ret; 976ce24b08aSFlorian Fainelli 9771942adf6SFlorian Fainelli /* Refuse deleting unused rules, and those that are not unique since 9781942adf6SFlorian Fainelli * that could leave IPv6 rules with one of the chained rule in the 9791942adf6SFlorian Fainelli * table. 9801942adf6SFlorian Fainelli */ 9811942adf6SFlorian Fainelli if (!test_bit(loc, priv->cfp.unique) || loc == 0) 9821942adf6SFlorian Fainelli return -EINVAL; 9831942adf6SFlorian Fainelli 984ae7a5affSFlorian Fainelli rule = bcm_sf2_cfp_rule_find(priv, port, loc); 985ae7a5affSFlorian Fainelli if (!rule) 986ae7a5affSFlorian Fainelli return -EINVAL; 987ae7a5affSFlorian Fainelli 988ce24b08aSFlorian Fainelli ret = bcm_sf2_cfp_rule_remove(priv, port, loc); 989ba0696c2SFlorian Fainelli 990ae7a5affSFlorian Fainelli list_del(&rule->next); 991ae7a5affSFlorian Fainelli kfree(rule); 992ae7a5affSFlorian Fainelli 993ba0696c2SFlorian Fainelli return ret; 994ba0696c2SFlorian Fainelli } 995ba0696c2SFlorian Fainelli 9967318166cSFlorian Fainelli static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow) 9977318166cSFlorian Fainelli { 9987318166cSFlorian Fainelli unsigned int i; 9997318166cSFlorian Fainelli 10007318166cSFlorian Fainelli for (i = 0; i < sizeof(flow->m_u); i++) 10017318166cSFlorian Fainelli flow->m_u.hdata[i] ^= 0xff; 10027318166cSFlorian Fainelli 10037318166cSFlorian Fainelli flow->m_ext.vlan_etype ^= cpu_to_be16(~0); 10047318166cSFlorian Fainelli flow->m_ext.vlan_tci ^= cpu_to_be16(~0); 10057318166cSFlorian Fainelli flow->m_ext.data[0] ^= cpu_to_be32(~0); 10067318166cSFlorian Fainelli flow->m_ext.data[1] ^= cpu_to_be32(~0); 10077318166cSFlorian Fainelli } 10087318166cSFlorian Fainelli 1009ae7a5affSFlorian Fainelli static int bcm_sf2_cfp_rule_get(struct bcm_sf2_priv *priv, int port, 1010ae7a5affSFlorian Fainelli struct ethtool_rxnfc *nfc) 1011ae7a5affSFlorian Fainelli { 1012ae7a5affSFlorian Fainelli struct cfp_rule *rule; 1013ae7a5affSFlorian Fainelli 1014ae7a5affSFlorian Fainelli rule = bcm_sf2_cfp_rule_find(priv, port, nfc->fs.location); 1015ae7a5affSFlorian Fainelli if (!rule) 1016ae7a5affSFlorian Fainelli return -EINVAL; 1017ae7a5affSFlorian Fainelli 1018ae7a5affSFlorian Fainelli memcpy(&nfc->fs, &rule->fs, sizeof(rule->fs)); 1019ae7a5affSFlorian Fainelli 1020ae7a5affSFlorian Fainelli bcm_sf2_invert_masks(&nfc->fs); 1021ae7a5affSFlorian Fainelli 1022ae7a5affSFlorian Fainelli /* Put the TCAM size here */ 1023ae7a5affSFlorian Fainelli nfc->data = bcm_sf2_cfp_rule_size(priv); 1024ae7a5affSFlorian Fainelli 1025ae7a5affSFlorian Fainelli return 0; 1026ae7a5affSFlorian Fainelli } 1027ae7a5affSFlorian Fainelli 10287318166cSFlorian Fainelli /* We implement the search doing a TCAM search operation */ 10297318166cSFlorian Fainelli static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv, 10307318166cSFlorian Fainelli int port, struct ethtool_rxnfc *nfc, 10317318166cSFlorian Fainelli u32 *rule_locs) 10327318166cSFlorian Fainelli { 10337318166cSFlorian Fainelli unsigned int index = 1, rules_cnt = 0; 10347318166cSFlorian Fainelli 1035ba0696c2SFlorian Fainelli for_each_set_bit_from(index, priv->cfp.unique, priv->num_cfp_rules) { 10367318166cSFlorian Fainelli rule_locs[rules_cnt] = index; 10377318166cSFlorian Fainelli rules_cnt++; 10387318166cSFlorian Fainelli } 10397318166cSFlorian Fainelli 10407318166cSFlorian Fainelli /* Put the TCAM size here */ 10417318166cSFlorian Fainelli nfc->data = bcm_sf2_cfp_rule_size(priv); 10427318166cSFlorian Fainelli nfc->rule_cnt = rules_cnt; 10437318166cSFlorian Fainelli 10447318166cSFlorian Fainelli return 0; 10457318166cSFlorian Fainelli } 10467318166cSFlorian Fainelli 10477318166cSFlorian Fainelli int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, 10487318166cSFlorian Fainelli struct ethtool_rxnfc *nfc, u32 *rule_locs) 10497318166cSFlorian Fainelli { 10508a75f4f2SFlorian Fainelli struct net_device *p = ds->ports[port].cpu_dp->master; 10517318166cSFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 10527318166cSFlorian Fainelli int ret = 0; 10537318166cSFlorian Fainelli 10547318166cSFlorian Fainelli mutex_lock(&priv->cfp.lock); 10557318166cSFlorian Fainelli 10567318166cSFlorian Fainelli switch (nfc->cmd) { 10577318166cSFlorian Fainelli case ETHTOOL_GRXCLSRLCNT: 10587318166cSFlorian Fainelli /* Subtract the default, unusable rule */ 1059ba0696c2SFlorian Fainelli nfc->rule_cnt = bitmap_weight(priv->cfp.unique, 1060df191632SFlorian Fainelli priv->num_cfp_rules) - 1; 10617318166cSFlorian Fainelli /* We support specifying rule locations */ 10627318166cSFlorian Fainelli nfc->data |= RX_CLS_LOC_SPECIAL; 10637318166cSFlorian Fainelli break; 10647318166cSFlorian Fainelli case ETHTOOL_GRXCLSRULE: 10654daa70cfSFlorian Fainelli ret = bcm_sf2_cfp_rule_get(priv, port, nfc); 10667318166cSFlorian Fainelli break; 10677318166cSFlorian Fainelli case ETHTOOL_GRXCLSRLALL: 10687318166cSFlorian Fainelli ret = bcm_sf2_cfp_rule_get_all(priv, port, nfc, rule_locs); 10697318166cSFlorian Fainelli break; 10707318166cSFlorian Fainelli default: 10717318166cSFlorian Fainelli ret = -EOPNOTSUPP; 10727318166cSFlorian Fainelli break; 10737318166cSFlorian Fainelli } 10747318166cSFlorian Fainelli 10757318166cSFlorian Fainelli mutex_unlock(&priv->cfp.lock); 10767318166cSFlorian Fainelli 10778a75f4f2SFlorian Fainelli if (ret) 10788a75f4f2SFlorian Fainelli return ret; 10798a75f4f2SFlorian Fainelli 10808a75f4f2SFlorian Fainelli /* Pass up the commands to the attached master network device */ 10818a75f4f2SFlorian Fainelli if (p->ethtool_ops->get_rxnfc) { 10828a75f4f2SFlorian Fainelli ret = p->ethtool_ops->get_rxnfc(p, nfc, rule_locs); 10838a75f4f2SFlorian Fainelli if (ret == -EOPNOTSUPP) 10848a75f4f2SFlorian Fainelli ret = 0; 10858a75f4f2SFlorian Fainelli } 10868a75f4f2SFlorian Fainelli 10877318166cSFlorian Fainelli return ret; 10887318166cSFlorian Fainelli } 10897318166cSFlorian Fainelli 10907318166cSFlorian Fainelli int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, 10917318166cSFlorian Fainelli struct ethtool_rxnfc *nfc) 10927318166cSFlorian Fainelli { 10938a75f4f2SFlorian Fainelli struct net_device *p = ds->ports[port].cpu_dp->master; 10947318166cSFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 10957318166cSFlorian Fainelli int ret = 0; 10967318166cSFlorian Fainelli 10977318166cSFlorian Fainelli mutex_lock(&priv->cfp.lock); 10987318166cSFlorian Fainelli 10997318166cSFlorian Fainelli switch (nfc->cmd) { 11007318166cSFlorian Fainelli case ETHTOOL_SRXCLSRLINS: 11017318166cSFlorian Fainelli ret = bcm_sf2_cfp_rule_set(ds, port, &nfc->fs); 11027318166cSFlorian Fainelli break; 11037318166cSFlorian Fainelli 11047318166cSFlorian Fainelli case ETHTOOL_SRXCLSRLDEL: 11057318166cSFlorian Fainelli ret = bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location); 11067318166cSFlorian Fainelli break; 11077318166cSFlorian Fainelli default: 11087318166cSFlorian Fainelli ret = -EOPNOTSUPP; 11097318166cSFlorian Fainelli break; 11107318166cSFlorian Fainelli } 11117318166cSFlorian Fainelli 11127318166cSFlorian Fainelli mutex_unlock(&priv->cfp.lock); 11137318166cSFlorian Fainelli 11148a75f4f2SFlorian Fainelli if (ret) 11158a75f4f2SFlorian Fainelli return ret; 11168a75f4f2SFlorian Fainelli 11178a75f4f2SFlorian Fainelli /* Pass up the commands to the attached master network device. 11188a75f4f2SFlorian Fainelli * This can fail, so rollback the operation if we need to. 11198a75f4f2SFlorian Fainelli */ 11208a75f4f2SFlorian Fainelli if (p->ethtool_ops->set_rxnfc) { 11218a75f4f2SFlorian Fainelli ret = p->ethtool_ops->set_rxnfc(p, nfc); 11228a75f4f2SFlorian Fainelli if (ret && ret != -EOPNOTSUPP) { 11238a75f4f2SFlorian Fainelli mutex_lock(&priv->cfp.lock); 11248a75f4f2SFlorian Fainelli bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location); 11258a75f4f2SFlorian Fainelli mutex_unlock(&priv->cfp.lock); 11268a75f4f2SFlorian Fainelli } else { 11278a75f4f2SFlorian Fainelli ret = 0; 11288a75f4f2SFlorian Fainelli } 11298a75f4f2SFlorian Fainelli } 11308a75f4f2SFlorian Fainelli 11317318166cSFlorian Fainelli return ret; 11327318166cSFlorian Fainelli } 11337318166cSFlorian Fainelli 11347318166cSFlorian Fainelli int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv) 11357318166cSFlorian Fainelli { 11367318166cSFlorian Fainelli unsigned int timeout = 1000; 11377318166cSFlorian Fainelli u32 reg; 11387318166cSFlorian Fainelli 11397318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 11407318166cSFlorian Fainelli reg |= TCAM_RESET; 11417318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_ACC); 11427318166cSFlorian Fainelli 11437318166cSFlorian Fainelli do { 11447318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 11457318166cSFlorian Fainelli if (!(reg & TCAM_RESET)) 11467318166cSFlorian Fainelli break; 11477318166cSFlorian Fainelli 11487318166cSFlorian Fainelli cpu_relax(); 11497318166cSFlorian Fainelli } while (timeout--); 11507318166cSFlorian Fainelli 11517318166cSFlorian Fainelli if (!timeout) 11527318166cSFlorian Fainelli return -ETIMEDOUT; 11537318166cSFlorian Fainelli 11547318166cSFlorian Fainelli return 0; 11557318166cSFlorian Fainelli } 1156ae7a5affSFlorian Fainelli 1157ae7a5affSFlorian Fainelli void bcm_sf2_cfp_exit(struct dsa_switch *ds) 1158ae7a5affSFlorian Fainelli { 1159ae7a5affSFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 1160ae7a5affSFlorian Fainelli struct cfp_rule *rule, *n; 1161ae7a5affSFlorian Fainelli 1162ae7a5affSFlorian Fainelli if (list_empty(&priv->cfp.rules_list)) 1163ae7a5affSFlorian Fainelli return; 1164ae7a5affSFlorian Fainelli 1165ae7a5affSFlorian Fainelli list_for_each_entry_safe_reverse(rule, n, &priv->cfp.rules_list, next) 1166ae7a5affSFlorian Fainelli bcm_sf2_cfp_rule_del(priv, rule->port, rule->fs.location); 1167ae7a5affSFlorian Fainelli } 11681c0130f0SFlorian Fainelli 11691c0130f0SFlorian Fainelli int bcm_sf2_cfp_resume(struct dsa_switch *ds) 11701c0130f0SFlorian Fainelli { 11711c0130f0SFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 11721c0130f0SFlorian Fainelli struct cfp_rule *rule; 11731c0130f0SFlorian Fainelli int ret = 0; 11741c0130f0SFlorian Fainelli u32 reg; 11751c0130f0SFlorian Fainelli 11761c0130f0SFlorian Fainelli if (list_empty(&priv->cfp.rules_list)) 11771c0130f0SFlorian Fainelli return ret; 11781c0130f0SFlorian Fainelli 11791c0130f0SFlorian Fainelli reg = core_readl(priv, CORE_CFP_CTL_REG); 11801c0130f0SFlorian Fainelli reg &= ~CFP_EN_MAP_MASK; 11811c0130f0SFlorian Fainelli core_writel(priv, reg, CORE_CFP_CTL_REG); 11821c0130f0SFlorian Fainelli 11831c0130f0SFlorian Fainelli ret = bcm_sf2_cfp_rst(priv); 11841c0130f0SFlorian Fainelli if (ret) 11851c0130f0SFlorian Fainelli return ret; 11861c0130f0SFlorian Fainelli 11871c0130f0SFlorian Fainelli list_for_each_entry(rule, &priv->cfp.rules_list, next) { 11881c0130f0SFlorian Fainelli ret = bcm_sf2_cfp_rule_remove(priv, rule->port, 11891c0130f0SFlorian Fainelli rule->fs.location); 11901c0130f0SFlorian Fainelli if (ret) { 11911c0130f0SFlorian Fainelli dev_err(ds->dev, "failed to remove rule\n"); 11921c0130f0SFlorian Fainelli return ret; 11931c0130f0SFlorian Fainelli } 11941c0130f0SFlorian Fainelli 11951c0130f0SFlorian Fainelli ret = bcm_sf2_cfp_rule_insert(ds, rule->port, &rule->fs); 11961c0130f0SFlorian Fainelli if (ret) { 11971c0130f0SFlorian Fainelli dev_err(ds->dev, "failed to restore rule\n"); 11981c0130f0SFlorian Fainelli return ret; 11991c0130f0SFlorian Fainelli } 1200f9086200Skbuild test robot } 12011c0130f0SFlorian Fainelli 12021c0130f0SFlorian Fainelli return ret; 12031c0130f0SFlorian Fainelli } 1204