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 235d80bcbbSFlorian Fainelli struct cfp_udf_slice_layout { 245d80bcbbSFlorian Fainelli u8 slices[UDFS_PER_SLICE]; 257318166cSFlorian Fainelli u32 mask_value; 265d80bcbbSFlorian Fainelli u32 base_offset; 277318166cSFlorian Fainelli }; 287318166cSFlorian Fainelli 295d80bcbbSFlorian Fainelli struct cfp_udf_layout { 305d80bcbbSFlorian Fainelli struct cfp_udf_slice_layout udfs[UDF_NUM_SLICES]; 315d80bcbbSFlorian Fainelli }; 325d80bcbbSFlorian Fainelli 335d80bcbbSFlorian Fainelli static const u8 zero_slice[UDFS_PER_SLICE] = { }; 345d80bcbbSFlorian Fainelli 357318166cSFlorian Fainelli /* UDF slices layout for a TCPv4/UDPv4 specification */ 367318166cSFlorian Fainelli static const struct cfp_udf_layout udf_tcpip4_layout = { 375d80bcbbSFlorian Fainelli .udfs = { 385d80bcbbSFlorian Fainelli [1] = { 397318166cSFlorian Fainelli .slices = { 407318166cSFlorian Fainelli /* End of L2, byte offset 12, src IP[0:15] */ 417318166cSFlorian Fainelli CFG_UDF_EOL2 | 6, 427318166cSFlorian Fainelli /* End of L2, byte offset 14, src IP[16:31] */ 437318166cSFlorian Fainelli CFG_UDF_EOL2 | 7, 447318166cSFlorian Fainelli /* End of L2, byte offset 16, dst IP[0:15] */ 457318166cSFlorian Fainelli CFG_UDF_EOL2 | 8, 467318166cSFlorian Fainelli /* End of L2, byte offset 18, dst IP[16:31] */ 477318166cSFlorian Fainelli CFG_UDF_EOL2 | 9, 487318166cSFlorian Fainelli /* End of L3, byte offset 0, src port */ 497318166cSFlorian Fainelli CFG_UDF_EOL3 | 0, 507318166cSFlorian Fainelli /* End of L3, byte offset 2, dst port */ 517318166cSFlorian Fainelli CFG_UDF_EOL3 | 1, 527318166cSFlorian Fainelli 0, 0, 0 537318166cSFlorian Fainelli }, 547318166cSFlorian Fainelli .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, 555d80bcbbSFlorian Fainelli .base_offset = CORE_UDF_0_A_0_8_PORT_0 + UDF_SLICE_OFFSET, 565d80bcbbSFlorian Fainelli }, 575d80bcbbSFlorian Fainelli }, 587318166cSFlorian Fainelli }; 597318166cSFlorian Fainelli 60ba0696c2SFlorian Fainelli /* UDF slices layout for a TCPv6/UDPv6 specification */ 61ba0696c2SFlorian Fainelli static const struct cfp_udf_layout udf_tcpip6_layout = { 62ba0696c2SFlorian Fainelli .udfs = { 63ba0696c2SFlorian Fainelli [0] = { 64ba0696c2SFlorian Fainelli .slices = { 65ba0696c2SFlorian Fainelli /* End of L2, byte offset 8, src IP[0:15] */ 66ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 4, 67ba0696c2SFlorian Fainelli /* End of L2, byte offset 10, src IP[16:31] */ 68ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 5, 69ba0696c2SFlorian Fainelli /* End of L2, byte offset 12, src IP[32:47] */ 70ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 6, 71ba0696c2SFlorian Fainelli /* End of L2, byte offset 14, src IP[48:63] */ 72ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 7, 73ba0696c2SFlorian Fainelli /* End of L2, byte offset 16, src IP[64:79] */ 74ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 8, 75ba0696c2SFlorian Fainelli /* End of L2, byte offset 18, src IP[80:95] */ 76ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 9, 77ba0696c2SFlorian Fainelli /* End of L2, byte offset 20, src IP[96:111] */ 78ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 10, 79ba0696c2SFlorian Fainelli /* End of L2, byte offset 22, src IP[112:127] */ 80ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 11, 81ba0696c2SFlorian Fainelli /* End of L3, byte offset 0, src port */ 82ba0696c2SFlorian Fainelli CFG_UDF_EOL3 | 0, 83ba0696c2SFlorian Fainelli }, 84ba0696c2SFlorian Fainelli .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, 85ba0696c2SFlorian Fainelli .base_offset = CORE_UDF_0_B_0_8_PORT_0, 86ba0696c2SFlorian Fainelli }, 87ba0696c2SFlorian Fainelli [3] = { 88ba0696c2SFlorian Fainelli .slices = { 89ba0696c2SFlorian Fainelli /* End of L2, byte offset 24, dst IP[0:15] */ 90ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 12, 91ba0696c2SFlorian Fainelli /* End of L2, byte offset 26, dst IP[16:31] */ 92ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 13, 93ba0696c2SFlorian Fainelli /* End of L2, byte offset 28, dst IP[32:47] */ 94ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 14, 95ba0696c2SFlorian Fainelli /* End of L2, byte offset 30, dst IP[48:63] */ 96ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 15, 97ba0696c2SFlorian Fainelli /* End of L2, byte offset 32, dst IP[64:79] */ 98ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 16, 99ba0696c2SFlorian Fainelli /* End of L2, byte offset 34, dst IP[80:95] */ 100ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 17, 101ba0696c2SFlorian Fainelli /* End of L2, byte offset 36, dst IP[96:111] */ 102ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 18, 103ba0696c2SFlorian Fainelli /* End of L2, byte offset 38, dst IP[112:127] */ 104ba0696c2SFlorian Fainelli CFG_UDF_EOL2 | 19, 105ba0696c2SFlorian Fainelli /* End of L3, byte offset 2, dst port */ 106ba0696c2SFlorian Fainelli CFG_UDF_EOL3 | 1, 107ba0696c2SFlorian Fainelli }, 108ba0696c2SFlorian Fainelli .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG, 109ba0696c2SFlorian Fainelli .base_offset = CORE_UDF_0_D_0_11_PORT_0, 110ba0696c2SFlorian Fainelli }, 111ba0696c2SFlorian Fainelli }, 112ba0696c2SFlorian Fainelli }; 113ba0696c2SFlorian Fainelli 1147318166cSFlorian Fainelli static inline unsigned int bcm_sf2_get_num_udf_slices(const u8 *layout) 1157318166cSFlorian Fainelli { 1167318166cSFlorian Fainelli unsigned int i, count = 0; 1177318166cSFlorian Fainelli 1185d80bcbbSFlorian Fainelli for (i = 0; i < UDFS_PER_SLICE; i++) { 1197318166cSFlorian Fainelli if (layout[i] != 0) 1207318166cSFlorian Fainelli count++; 1217318166cSFlorian Fainelli } 1227318166cSFlorian Fainelli 1237318166cSFlorian Fainelli return count; 1247318166cSFlorian Fainelli } 1257318166cSFlorian Fainelli 1265d80bcbbSFlorian Fainelli static inline u32 udf_upper_bits(unsigned int num_udf) 1277318166cSFlorian Fainelli { 1285d80bcbbSFlorian Fainelli return GENMASK(num_udf - 1, 0) >> (UDFS_PER_SLICE - 1); 1295d80bcbbSFlorian Fainelli } 1305d80bcbbSFlorian Fainelli 1315d80bcbbSFlorian Fainelli static inline u32 udf_lower_bits(unsigned int num_udf) 1325d80bcbbSFlorian Fainelli { 1335d80bcbbSFlorian Fainelli return (u8)GENMASK(num_udf - 1, 0); 1345d80bcbbSFlorian Fainelli } 1355d80bcbbSFlorian Fainelli 1365d80bcbbSFlorian Fainelli static unsigned int bcm_sf2_get_slice_number(const struct cfp_udf_layout *l, 1375d80bcbbSFlorian Fainelli unsigned int start) 1385d80bcbbSFlorian Fainelli { 1395d80bcbbSFlorian Fainelli const struct cfp_udf_slice_layout *slice_layout; 1405d80bcbbSFlorian Fainelli unsigned int slice_idx; 1415d80bcbbSFlorian Fainelli 1425d80bcbbSFlorian Fainelli for (slice_idx = start; slice_idx < UDF_NUM_SLICES; slice_idx++) { 1435d80bcbbSFlorian Fainelli slice_layout = &l->udfs[slice_idx]; 1445d80bcbbSFlorian Fainelli if (memcmp(slice_layout->slices, zero_slice, 1455d80bcbbSFlorian Fainelli sizeof(zero_slice))) 1465d80bcbbSFlorian Fainelli break; 1475d80bcbbSFlorian Fainelli } 1485d80bcbbSFlorian Fainelli 1495d80bcbbSFlorian Fainelli return slice_idx; 1505d80bcbbSFlorian Fainelli } 1515d80bcbbSFlorian Fainelli 1525d80bcbbSFlorian Fainelli static void bcm_sf2_cfp_udf_set(struct bcm_sf2_priv *priv, 1535d80bcbbSFlorian Fainelli const struct cfp_udf_layout *layout, 1545d80bcbbSFlorian Fainelli unsigned int slice_num) 1555d80bcbbSFlorian Fainelli { 1565d80bcbbSFlorian Fainelli u32 offset = layout->udfs[slice_num].base_offset; 1577318166cSFlorian Fainelli unsigned int i; 1587318166cSFlorian Fainelli 1595d80bcbbSFlorian Fainelli for (i = 0; i < UDFS_PER_SLICE; i++) 1605d80bcbbSFlorian Fainelli core_writel(priv, layout->udfs[slice_num].slices[i], 1615d80bcbbSFlorian Fainelli offset + i * 4); 1627318166cSFlorian Fainelli } 1637318166cSFlorian Fainelli 1647318166cSFlorian Fainelli static int bcm_sf2_cfp_op(struct bcm_sf2_priv *priv, unsigned int op) 1657318166cSFlorian Fainelli { 1667318166cSFlorian Fainelli unsigned int timeout = 1000; 1677318166cSFlorian Fainelli u32 reg; 1687318166cSFlorian Fainelli 1697318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 1707318166cSFlorian Fainelli reg &= ~(OP_SEL_MASK | RAM_SEL_MASK); 1717318166cSFlorian Fainelli reg |= OP_STR_DONE | op; 1727318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_ACC); 1737318166cSFlorian Fainelli 1747318166cSFlorian Fainelli do { 1757318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 1767318166cSFlorian Fainelli if (!(reg & OP_STR_DONE)) 1777318166cSFlorian Fainelli break; 1787318166cSFlorian Fainelli 1797318166cSFlorian Fainelli cpu_relax(); 1807318166cSFlorian Fainelli } while (timeout--); 1817318166cSFlorian Fainelli 1827318166cSFlorian Fainelli if (!timeout) 1837318166cSFlorian Fainelli return -ETIMEDOUT; 1847318166cSFlorian Fainelli 1857318166cSFlorian Fainelli return 0; 1867318166cSFlorian Fainelli } 1877318166cSFlorian Fainelli 1887318166cSFlorian Fainelli static inline void bcm_sf2_cfp_rule_addr_set(struct bcm_sf2_priv *priv, 1897318166cSFlorian Fainelli unsigned int addr) 1907318166cSFlorian Fainelli { 1917318166cSFlorian Fainelli u32 reg; 1927318166cSFlorian Fainelli 193df191632SFlorian Fainelli WARN_ON(addr >= priv->num_cfp_rules); 1947318166cSFlorian Fainelli 1957318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 1967318166cSFlorian Fainelli reg &= ~(XCESS_ADDR_MASK << XCESS_ADDR_SHIFT); 1977318166cSFlorian Fainelli reg |= addr << XCESS_ADDR_SHIFT; 1987318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_ACC); 1997318166cSFlorian Fainelli } 2007318166cSFlorian Fainelli 2017318166cSFlorian Fainelli static inline unsigned int bcm_sf2_cfp_rule_size(struct bcm_sf2_priv *priv) 2027318166cSFlorian Fainelli { 2037318166cSFlorian Fainelli /* Entry #0 is reserved */ 204df191632SFlorian Fainelli return priv->num_cfp_rules - 1; 2057318166cSFlorian Fainelli } 2067318166cSFlorian Fainelli 20733061458SFlorian Fainelli static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv, 20833061458SFlorian Fainelli unsigned int rule_index, 20933061458SFlorian Fainelli unsigned int port_num, 210ba0696c2SFlorian Fainelli unsigned int queue_num, 211ba0696c2SFlorian Fainelli bool fwd_map_change) 21233061458SFlorian Fainelli { 21333061458SFlorian Fainelli int ret; 21433061458SFlorian Fainelli u32 reg; 21533061458SFlorian Fainelli 21633061458SFlorian Fainelli /* Replace ARL derived destination with DST_MAP derived, define 21733061458SFlorian Fainelli * which port and queue this should be forwarded to. 21833061458SFlorian Fainelli */ 219ba0696c2SFlorian Fainelli if (fwd_map_change) 220ba0696c2SFlorian Fainelli reg = CHANGE_FWRD_MAP_IB_REP_ARL | 221ba0696c2SFlorian Fainelli BIT(port_num + DST_MAP_IB_SHIFT) | 22233061458SFlorian Fainelli CHANGE_TC | queue_num << NEW_TC_SHIFT; 223ba0696c2SFlorian Fainelli else 224ba0696c2SFlorian Fainelli reg = 0; 22533061458SFlorian Fainelli 22633061458SFlorian Fainelli core_writel(priv, reg, CORE_ACT_POL_DATA0); 22733061458SFlorian Fainelli 22833061458SFlorian Fainelli /* Set classification ID that needs to be put in Broadcom tag */ 229ba0696c2SFlorian Fainelli core_writel(priv, rule_index << CHAIN_ID_SHIFT, CORE_ACT_POL_DATA1); 23033061458SFlorian Fainelli 23133061458SFlorian Fainelli core_writel(priv, 0, CORE_ACT_POL_DATA2); 23233061458SFlorian Fainelli 23333061458SFlorian Fainelli /* Configure policer RAM now */ 23433061458SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | ACT_POL_RAM); 23533061458SFlorian Fainelli if (ret) { 23633061458SFlorian Fainelli pr_err("Policer entry at %d failed\n", rule_index); 23733061458SFlorian Fainelli return ret; 23833061458SFlorian Fainelli } 23933061458SFlorian Fainelli 24033061458SFlorian Fainelli /* Disable the policer */ 24133061458SFlorian Fainelli core_writel(priv, POLICER_MODE_DISABLE, CORE_RATE_METER0); 24233061458SFlorian Fainelli 24333061458SFlorian Fainelli /* Now the rate meter */ 24433061458SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | RATE_METER_RAM); 24533061458SFlorian Fainelli if (ret) { 24633061458SFlorian Fainelli pr_err("Meter entry at %d failed\n", rule_index); 24733061458SFlorian Fainelli return ret; 24833061458SFlorian Fainelli } 24933061458SFlorian Fainelli 25033061458SFlorian Fainelli return 0; 25133061458SFlorian Fainelli } 25233061458SFlorian Fainelli 253bc3fc44cSFlorian Fainelli static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv, 254bc3fc44cSFlorian Fainelli struct ethtool_tcpip4_spec *v4_spec, 255bc3fc44cSFlorian Fainelli unsigned int slice_num, 256bc3fc44cSFlorian Fainelli bool mask) 257bc3fc44cSFlorian Fainelli { 258bc3fc44cSFlorian Fainelli u32 reg, offset; 259bc3fc44cSFlorian Fainelli 260bc3fc44cSFlorian Fainelli /* C-Tag [31:24] 261bc3fc44cSFlorian Fainelli * UDF_n_A8 [23:8] 262bc3fc44cSFlorian Fainelli * UDF_n_A7 [7:0] 263bc3fc44cSFlorian Fainelli */ 264bc3fc44cSFlorian Fainelli reg = 0; 265bc3fc44cSFlorian Fainelli if (mask) 266bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(4); 267bc3fc44cSFlorian Fainelli else 268bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(4); 269bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 270bc3fc44cSFlorian Fainelli 271bc3fc44cSFlorian Fainelli /* UDF_n_A7 [31:24] 272bc3fc44cSFlorian Fainelli * UDF_n_A6 [23:8] 273bc3fc44cSFlorian Fainelli * UDF_n_A5 [7:0] 274bc3fc44cSFlorian Fainelli */ 275bc3fc44cSFlorian Fainelli reg = be16_to_cpu(v4_spec->pdst) >> 8; 276bc3fc44cSFlorian Fainelli if (mask) 277bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(3); 278bc3fc44cSFlorian Fainelli else 279bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(3); 280bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 281bc3fc44cSFlorian Fainelli 282bc3fc44cSFlorian Fainelli /* UDF_n_A5 [31:24] 283bc3fc44cSFlorian Fainelli * UDF_n_A4 [23:8] 284bc3fc44cSFlorian Fainelli * UDF_n_A3 [7:0] 285bc3fc44cSFlorian Fainelli */ 286bc3fc44cSFlorian Fainelli reg = (be16_to_cpu(v4_spec->pdst) & 0xff) << 24 | 287bc3fc44cSFlorian Fainelli (u32)be16_to_cpu(v4_spec->psrc) << 8 | 288bc3fc44cSFlorian Fainelli (be32_to_cpu(v4_spec->ip4dst) & 0x0000ff00) >> 8; 289bc3fc44cSFlorian Fainelli if (mask) 290bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(2); 291bc3fc44cSFlorian Fainelli else 292bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(2); 293bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 294bc3fc44cSFlorian Fainelli 295bc3fc44cSFlorian Fainelli /* UDF_n_A3 [31:24] 296bc3fc44cSFlorian Fainelli * UDF_n_A2 [23:8] 297bc3fc44cSFlorian Fainelli * UDF_n_A1 [7:0] 298bc3fc44cSFlorian Fainelli */ 299bc3fc44cSFlorian Fainelli reg = (u32)(be32_to_cpu(v4_spec->ip4dst) & 0xff) << 24 | 300bc3fc44cSFlorian Fainelli (u32)(be32_to_cpu(v4_spec->ip4dst) >> 16) << 8 | 301bc3fc44cSFlorian Fainelli (be32_to_cpu(v4_spec->ip4src) & 0x0000ff00) >> 8; 302bc3fc44cSFlorian Fainelli if (mask) 303bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(1); 304bc3fc44cSFlorian Fainelli else 305bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(1); 306bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 307bc3fc44cSFlorian Fainelli 308bc3fc44cSFlorian Fainelli /* UDF_n_A1 [31:24] 309bc3fc44cSFlorian Fainelli * UDF_n_A0 [23:8] 310bc3fc44cSFlorian Fainelli * Reserved [7:4] 311bc3fc44cSFlorian Fainelli * Slice ID [3:2] 312bc3fc44cSFlorian Fainelli * Slice valid [1:0] 313bc3fc44cSFlorian Fainelli */ 314bc3fc44cSFlorian Fainelli reg = (u32)(be32_to_cpu(v4_spec->ip4src) & 0xff) << 24 | 315bc3fc44cSFlorian Fainelli (u32)(be32_to_cpu(v4_spec->ip4src) >> 16) << 8 | 316bc3fc44cSFlorian Fainelli SLICE_NUM(slice_num) | SLICE_VALID; 317bc3fc44cSFlorian Fainelli if (mask) 318bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(0); 319bc3fc44cSFlorian Fainelli else 320bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(0); 321bc3fc44cSFlorian Fainelli core_writel(priv, reg, offset); 322bc3fc44cSFlorian Fainelli } 323bc3fc44cSFlorian Fainelli 32433061458SFlorian Fainelli static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port, 32533061458SFlorian Fainelli unsigned int port_num, 32633061458SFlorian Fainelli unsigned int queue_num, 3277318166cSFlorian Fainelli struct ethtool_rx_flow_spec *fs) 3287318166cSFlorian Fainelli { 329bc3fc44cSFlorian Fainelli struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec; 3307318166cSFlorian Fainelli const struct cfp_udf_layout *layout; 3317318166cSFlorian Fainelli unsigned int slice_num, rule_index; 3327318166cSFlorian Fainelli u8 ip_proto, ip_frag; 3337318166cSFlorian Fainelli u8 num_udf; 3347318166cSFlorian Fainelli u32 reg; 3357318166cSFlorian Fainelli int ret; 3367318166cSFlorian Fainelli 3377318166cSFlorian Fainelli switch (fs->flow_type & ~FLOW_EXT) { 3387318166cSFlorian Fainelli case TCP_V4_FLOW: 3397318166cSFlorian Fainelli ip_proto = IPPROTO_TCP; 3407318166cSFlorian Fainelli v4_spec = &fs->h_u.tcp_ip4_spec; 341bc3fc44cSFlorian Fainelli v4_m_spec = &fs->m_u.tcp_ip4_spec; 3427318166cSFlorian Fainelli break; 3437318166cSFlorian Fainelli case UDP_V4_FLOW: 3447318166cSFlorian Fainelli ip_proto = IPPROTO_UDP; 3457318166cSFlorian Fainelli v4_spec = &fs->h_u.udp_ip4_spec; 346bc3fc44cSFlorian Fainelli v4_m_spec = &fs->m_u.udp_ip4_spec; 3477318166cSFlorian Fainelli break; 3487318166cSFlorian Fainelli default: 3497318166cSFlorian Fainelli return -EINVAL; 3507318166cSFlorian Fainelli } 3517318166cSFlorian Fainelli 35233061458SFlorian Fainelli ip_frag = be32_to_cpu(fs->m_ext.data[0]); 35333061458SFlorian Fainelli 35433061458SFlorian Fainelli /* Locate the first rule available */ 35533061458SFlorian Fainelli if (fs->location == RX_CLS_LOC_ANY) 35633061458SFlorian Fainelli rule_index = find_first_zero_bit(priv->cfp.used, 35733061458SFlorian Fainelli bcm_sf2_cfp_rule_size(priv)); 35833061458SFlorian Fainelli else 35933061458SFlorian Fainelli rule_index = fs->location; 36033061458SFlorian Fainelli 3617318166cSFlorian Fainelli layout = &udf_tcpip4_layout; 3625d80bcbbSFlorian Fainelli /* We only use one UDF slice for now */ 3635d80bcbbSFlorian Fainelli slice_num = bcm_sf2_get_slice_number(layout, 0); 3645d80bcbbSFlorian Fainelli if (slice_num == UDF_NUM_SLICES) 3655d80bcbbSFlorian Fainelli return -EINVAL; 3665d80bcbbSFlorian Fainelli 3675d80bcbbSFlorian Fainelli num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); 3687318166cSFlorian Fainelli 3697318166cSFlorian Fainelli /* Apply the UDF layout for this filter */ 3705d80bcbbSFlorian Fainelli bcm_sf2_cfp_udf_set(priv, layout, slice_num); 3717318166cSFlorian Fainelli 3727318166cSFlorian Fainelli /* Apply to all packets received through this port */ 3737318166cSFlorian Fainelli core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7)); 3747318166cSFlorian Fainelli 37533061458SFlorian Fainelli /* Source port map match */ 37633061458SFlorian Fainelli core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7)); 37733061458SFlorian Fainelli 3787318166cSFlorian Fainelli /* S-Tag status [31:30] 3797318166cSFlorian Fainelli * C-Tag status [29:28] 3807318166cSFlorian Fainelli * L2 framing [27:26] 3817318166cSFlorian Fainelli * L3 framing [25:24] 3827318166cSFlorian Fainelli * IP ToS [23:16] 3837318166cSFlorian Fainelli * IP proto [15:08] 3847318166cSFlorian Fainelli * IP Fragm [7] 3857318166cSFlorian Fainelli * Non 1st frag [6] 3867318166cSFlorian Fainelli * IP Authen [5] 3877318166cSFlorian Fainelli * TTL range [4:3] 3887318166cSFlorian Fainelli * PPPoE session [2] 3897318166cSFlorian Fainelli * Reserved [1] 3907318166cSFlorian Fainelli * UDF_Valid[8] [0] 3917318166cSFlorian Fainelli */ 39239cdd349SFlorian Fainelli core_writel(priv, v4_spec->tos << IPTOS_SHIFT | 3935d80bcbbSFlorian Fainelli ip_proto << IPPROTO_SHIFT | ip_frag << IP_FRAG_SHIFT | 3945d80bcbbSFlorian Fainelli udf_upper_bits(num_udf), 3957318166cSFlorian Fainelli CORE_CFP_DATA_PORT(6)); 3967318166cSFlorian Fainelli 397bc3fc44cSFlorian Fainelli /* Mask with the specific layout for IPv4 packets */ 398bc3fc44cSFlorian Fainelli core_writel(priv, layout->udfs[slice_num].mask_value | 399bc3fc44cSFlorian Fainelli udf_upper_bits(num_udf), CORE_CFP_MASK_PORT(6)); 400bc3fc44cSFlorian Fainelli 4017318166cSFlorian Fainelli /* UDF_Valid[7:0] [31:24] 4027318166cSFlorian Fainelli * S-Tag [23:8] 4037318166cSFlorian Fainelli * C-Tag [7:0] 4047318166cSFlorian Fainelli */ 4055d80bcbbSFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); 4067318166cSFlorian Fainelli 4077318166cSFlorian Fainelli /* Mask all but valid UDFs */ 4085d80bcbbSFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); 4097318166cSFlorian Fainelli 410bc3fc44cSFlorian Fainelli /* Program the match and the mask */ 411bc3fc44cSFlorian Fainelli bcm_sf2_cfp_slice_ipv4(priv, v4_spec, slice_num, false); 412bc3fc44cSFlorian Fainelli bcm_sf2_cfp_slice_ipv4(priv, v4_m_spec, SLICE_NUM_MASK, true); 4137318166cSFlorian Fainelli 4147318166cSFlorian Fainelli /* Insert into TCAM now */ 4157318166cSFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, rule_index); 4167318166cSFlorian Fainelli 4177318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 4187318166cSFlorian Fainelli if (ret) { 4197318166cSFlorian Fainelli pr_err("TCAM entry at addr %d failed\n", rule_index); 4207318166cSFlorian Fainelli return ret; 4217318166cSFlorian Fainelli } 4227318166cSFlorian Fainelli 42333061458SFlorian Fainelli /* Insert into Action and policer RAMs now */ 424ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_act_pol_set(priv, rule_index, port_num, 425ba0696c2SFlorian Fainelli queue_num, true); 42633061458SFlorian Fainelli if (ret) 4277318166cSFlorian Fainelli return ret; 4287318166cSFlorian Fainelli 4297318166cSFlorian Fainelli /* Turn on CFP for this rule now */ 4307318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_CTL_REG); 4317318166cSFlorian Fainelli reg |= BIT(port); 4327318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_CTL_REG); 4337318166cSFlorian Fainelli 4347318166cSFlorian Fainelli /* Flag the rule as being used and return it */ 4357318166cSFlorian Fainelli set_bit(rule_index, priv->cfp.used); 436ba0696c2SFlorian Fainelli set_bit(rule_index, priv->cfp.unique); 4377318166cSFlorian Fainelli fs->location = rule_index; 4387318166cSFlorian Fainelli 4397318166cSFlorian Fainelli return 0; 4407318166cSFlorian Fainelli } 4417318166cSFlorian Fainelli 442ba0696c2SFlorian Fainelli static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv, 443ba0696c2SFlorian Fainelli const __be32 *ip6_addr, const __be16 port, 444ba0696c2SFlorian Fainelli unsigned int slice_num) 445ba0696c2SFlorian Fainelli { 446ba0696c2SFlorian Fainelli u32 reg, tmp, val; 447ba0696c2SFlorian Fainelli 448ba0696c2SFlorian Fainelli /* C-Tag [31:24] 449ba0696c2SFlorian Fainelli * UDF_n_B8 [23:8] (port) 450ba0696c2SFlorian Fainelli * UDF_n_B7 (upper) [7:0] (addr[15:8]) 451ba0696c2SFlorian Fainelli */ 452ba0696c2SFlorian Fainelli reg = be32_to_cpu(ip6_addr[3]); 453ba0696c2SFlorian Fainelli val = (u32)be16_to_cpu(port) << 8 | ((reg >> 8) & 0xff); 454ba0696c2SFlorian Fainelli core_writel(priv, val, CORE_CFP_DATA_PORT(4)); 455ba0696c2SFlorian Fainelli 456ba0696c2SFlorian Fainelli /* UDF_n_B7 (lower) [31:24] (addr[7:0]) 457ba0696c2SFlorian Fainelli * UDF_n_B6 [23:8] (addr[31:16]) 458ba0696c2SFlorian Fainelli * UDF_n_B5 (upper) [7:0] (addr[47:40]) 459ba0696c2SFlorian Fainelli */ 460ba0696c2SFlorian Fainelli tmp = be32_to_cpu(ip6_addr[2]); 461ba0696c2SFlorian Fainelli val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 | 462ba0696c2SFlorian Fainelli ((tmp >> 8) & 0xff); 463ba0696c2SFlorian Fainelli core_writel(priv, val, CORE_CFP_DATA_PORT(3)); 464ba0696c2SFlorian Fainelli 465ba0696c2SFlorian Fainelli /* UDF_n_B5 (lower) [31:24] (addr[39:32]) 466ba0696c2SFlorian Fainelli * UDF_n_B4 [23:8] (addr[63:48]) 467ba0696c2SFlorian Fainelli * UDF_n_B3 (upper) [7:0] (addr[79:72]) 468ba0696c2SFlorian Fainelli */ 469ba0696c2SFlorian Fainelli reg = be32_to_cpu(ip6_addr[1]); 470ba0696c2SFlorian Fainelli val = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 | 471ba0696c2SFlorian Fainelli ((reg >> 8) & 0xff); 472ba0696c2SFlorian Fainelli core_writel(priv, val, CORE_CFP_DATA_PORT(2)); 473ba0696c2SFlorian Fainelli 474ba0696c2SFlorian Fainelli /* UDF_n_B3 (lower) [31:24] (addr[71:64]) 475ba0696c2SFlorian Fainelli * UDF_n_B2 [23:8] (addr[95:80]) 476ba0696c2SFlorian Fainelli * UDF_n_B1 (upper) [7:0] (addr[111:104]) 477ba0696c2SFlorian Fainelli */ 478ba0696c2SFlorian Fainelli tmp = be32_to_cpu(ip6_addr[0]); 479ba0696c2SFlorian Fainelli val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 | 480ba0696c2SFlorian Fainelli ((tmp >> 8) & 0xff); 481ba0696c2SFlorian Fainelli core_writel(priv, val, CORE_CFP_DATA_PORT(1)); 482ba0696c2SFlorian Fainelli 483ba0696c2SFlorian Fainelli /* UDF_n_B1 (lower) [31:24] (addr[103:96]) 484ba0696c2SFlorian Fainelli * UDF_n_B0 [23:8] (addr[127:112]) 485ba0696c2SFlorian Fainelli * Reserved [7:4] 486ba0696c2SFlorian Fainelli * Slice ID [3:2] 487ba0696c2SFlorian Fainelli * Slice valid [1:0] 488ba0696c2SFlorian Fainelli */ 489ba0696c2SFlorian Fainelli reg = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 | 490ba0696c2SFlorian Fainelli SLICE_NUM(slice_num) | SLICE_VALID; 491ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(0)); 492ba0696c2SFlorian Fainelli 493ba0696c2SFlorian Fainelli /* All other UDFs should be matched with the filter */ 494ba0696c2SFlorian Fainelli core_writel(priv, 0x00ffffff, CORE_CFP_MASK_PORT(4)); 495ba0696c2SFlorian Fainelli core_writel(priv, 0xffffffff, CORE_CFP_MASK_PORT(3)); 496ba0696c2SFlorian Fainelli core_writel(priv, 0xffffffff, CORE_CFP_MASK_PORT(2)); 497ba0696c2SFlorian Fainelli core_writel(priv, 0xffffffff, CORE_CFP_MASK_PORT(1)); 498ba0696c2SFlorian Fainelli core_writel(priv, 0xffffff0f, CORE_CFP_MASK_PORT(0)); 499ba0696c2SFlorian Fainelli } 500ba0696c2SFlorian Fainelli 501ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, 502ba0696c2SFlorian Fainelli unsigned int port_num, 503ba0696c2SFlorian Fainelli unsigned int queue_num, 504ba0696c2SFlorian Fainelli struct ethtool_rx_flow_spec *fs) 505ba0696c2SFlorian Fainelli { 506ba0696c2SFlorian Fainelli unsigned int slice_num, rule_index[2]; 507ba0696c2SFlorian Fainelli struct ethtool_tcpip6_spec *v6_spec; 508ba0696c2SFlorian Fainelli const struct cfp_udf_layout *layout; 509ba0696c2SFlorian Fainelli u8 ip_proto, ip_frag; 510ba0696c2SFlorian Fainelli int ret = 0; 511ba0696c2SFlorian Fainelli u8 num_udf; 512ba0696c2SFlorian Fainelli u32 reg; 513ba0696c2SFlorian Fainelli 514ba0696c2SFlorian Fainelli switch (fs->flow_type & ~FLOW_EXT) { 515ba0696c2SFlorian Fainelli case TCP_V6_FLOW: 516ba0696c2SFlorian Fainelli ip_proto = IPPROTO_TCP; 517ba0696c2SFlorian Fainelli v6_spec = &fs->h_u.tcp_ip6_spec; 518ba0696c2SFlorian Fainelli break; 519ba0696c2SFlorian Fainelli case UDP_V6_FLOW: 520ba0696c2SFlorian Fainelli ip_proto = IPPROTO_UDP; 521ba0696c2SFlorian Fainelli v6_spec = &fs->h_u.udp_ip6_spec; 522ba0696c2SFlorian Fainelli break; 523ba0696c2SFlorian Fainelli default: 524ba0696c2SFlorian Fainelli return -EINVAL; 525ba0696c2SFlorian Fainelli } 526ba0696c2SFlorian Fainelli 527ba0696c2SFlorian Fainelli ip_frag = be32_to_cpu(fs->m_ext.data[0]); 528ba0696c2SFlorian Fainelli 529ba0696c2SFlorian Fainelli layout = &udf_tcpip6_layout; 530ba0696c2SFlorian Fainelli slice_num = bcm_sf2_get_slice_number(layout, 0); 531ba0696c2SFlorian Fainelli if (slice_num == UDF_NUM_SLICES) 532ba0696c2SFlorian Fainelli return -EINVAL; 533ba0696c2SFlorian Fainelli 534ba0696c2SFlorian Fainelli num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); 535ba0696c2SFlorian Fainelli 536ba0696c2SFlorian Fainelli /* Negotiate two indexes, one for the second half which we are chained 537ba0696c2SFlorian Fainelli * from, which is what we will return to user-space, and a second one 538ba0696c2SFlorian Fainelli * which is used to store its first half. That first half does not 539ba0696c2SFlorian Fainelli * allow any choice of placement, so it just needs to find the next 540ba0696c2SFlorian Fainelli * available bit. We return the second half as fs->location because 541ba0696c2SFlorian Fainelli * that helps with the rule lookup later on since the second half is 542ba0696c2SFlorian Fainelli * chained from its first half, we can easily identify IPv6 CFP rules 543ba0696c2SFlorian Fainelli * by looking whether they carry a CHAIN_ID. 544ba0696c2SFlorian Fainelli * 545ba0696c2SFlorian Fainelli * We also want the second half to have a lower rule_index than its 546ba0696c2SFlorian Fainelli * first half because the HW search is by incrementing addresses. 547ba0696c2SFlorian Fainelli */ 548ba0696c2SFlorian Fainelli if (fs->location == RX_CLS_LOC_ANY) 549ba0696c2SFlorian Fainelli rule_index[0] = find_first_zero_bit(priv->cfp.used, 550ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_size(priv)); 551ba0696c2SFlorian Fainelli else 552ba0696c2SFlorian Fainelli rule_index[0] = fs->location; 553ba0696c2SFlorian Fainelli 554ba0696c2SFlorian Fainelli /* Flag it as used (cleared on error path) such that we can immediately 555ba0696c2SFlorian Fainelli * obtain a second one to chain from. 556ba0696c2SFlorian Fainelli */ 557ba0696c2SFlorian Fainelli set_bit(rule_index[0], priv->cfp.used); 558ba0696c2SFlorian Fainelli 559ba0696c2SFlorian Fainelli rule_index[1] = find_first_zero_bit(priv->cfp.used, 560ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_size(priv)); 561ba0696c2SFlorian Fainelli if (rule_index[1] > bcm_sf2_cfp_rule_size(priv)) { 562ba0696c2SFlorian Fainelli ret = -ENOSPC; 563ba0696c2SFlorian Fainelli goto out_err; 564ba0696c2SFlorian Fainelli } 565ba0696c2SFlorian Fainelli 566ba0696c2SFlorian Fainelli /* Apply the UDF layout for this filter */ 567ba0696c2SFlorian Fainelli bcm_sf2_cfp_udf_set(priv, layout, slice_num); 568ba0696c2SFlorian Fainelli 569ba0696c2SFlorian Fainelli /* Apply to all packets received through this port */ 570ba0696c2SFlorian Fainelli core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7)); 571ba0696c2SFlorian Fainelli 572ba0696c2SFlorian Fainelli /* Source port map match */ 573ba0696c2SFlorian Fainelli core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7)); 574ba0696c2SFlorian Fainelli 575ba0696c2SFlorian Fainelli /* S-Tag status [31:30] 576ba0696c2SFlorian Fainelli * C-Tag status [29:28] 577ba0696c2SFlorian Fainelli * L2 framing [27:26] 578ba0696c2SFlorian Fainelli * L3 framing [25:24] 579ba0696c2SFlorian Fainelli * IP ToS [23:16] 580ba0696c2SFlorian Fainelli * IP proto [15:08] 581ba0696c2SFlorian Fainelli * IP Fragm [7] 582ba0696c2SFlorian Fainelli * Non 1st frag [6] 583ba0696c2SFlorian Fainelli * IP Authen [5] 584ba0696c2SFlorian Fainelli * TTL range [4:3] 585ba0696c2SFlorian Fainelli * PPPoE session [2] 586ba0696c2SFlorian Fainelli * Reserved [1] 587ba0696c2SFlorian Fainelli * UDF_Valid[8] [0] 588ba0696c2SFlorian Fainelli */ 589ba0696c2SFlorian Fainelli reg = 1 << L3_FRAMING_SHIFT | ip_proto << IPPROTO_SHIFT | 590ba0696c2SFlorian Fainelli ip_frag << IP_FRAG_SHIFT | udf_upper_bits(num_udf); 591ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(6)); 592ba0696c2SFlorian Fainelli 593ba0696c2SFlorian Fainelli /* Mask with the specific layout for IPv6 packets including 594ba0696c2SFlorian Fainelli * UDF_Valid[8] 595ba0696c2SFlorian Fainelli */ 596ba0696c2SFlorian Fainelli reg = layout->udfs[slice_num].mask_value | udf_upper_bits(num_udf); 597ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); 598ba0696c2SFlorian Fainelli 599ba0696c2SFlorian Fainelli /* UDF_Valid[7:0] [31:24] 600ba0696c2SFlorian Fainelli * S-Tag [23:8] 601ba0696c2SFlorian Fainelli * C-Tag [7:0] 602ba0696c2SFlorian Fainelli */ 603ba0696c2SFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); 604ba0696c2SFlorian Fainelli 605ba0696c2SFlorian Fainelli /* Mask all but valid UDFs */ 606ba0696c2SFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); 607ba0696c2SFlorian Fainelli 608ba0696c2SFlorian Fainelli /* Slice the IPv6 source address and port */ 609ba0696c2SFlorian Fainelli bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6src, v6_spec->psrc, slice_num); 610ba0696c2SFlorian Fainelli 611ba0696c2SFlorian Fainelli /* Insert into TCAM now because we need to insert a second rule */ 612ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, rule_index[0]); 613ba0696c2SFlorian Fainelli 614ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 615ba0696c2SFlorian Fainelli if (ret) { 616ba0696c2SFlorian Fainelli pr_err("TCAM entry at addr %d failed\n", rule_index[0]); 617ba0696c2SFlorian Fainelli goto out_err; 618ba0696c2SFlorian Fainelli } 619ba0696c2SFlorian Fainelli 620ba0696c2SFlorian Fainelli /* Insert into Action and policer RAMs now */ 621ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port_num, 622ba0696c2SFlorian Fainelli queue_num, false); 623ba0696c2SFlorian Fainelli if (ret) 624ba0696c2SFlorian Fainelli goto out_err; 625ba0696c2SFlorian Fainelli 626ba0696c2SFlorian Fainelli /* Now deal with the second slice to chain this rule */ 627ba0696c2SFlorian Fainelli slice_num = bcm_sf2_get_slice_number(layout, slice_num + 1); 628ba0696c2SFlorian Fainelli if (slice_num == UDF_NUM_SLICES) { 629ba0696c2SFlorian Fainelli ret = -EINVAL; 630ba0696c2SFlorian Fainelli goto out_err; 631ba0696c2SFlorian Fainelli } 632ba0696c2SFlorian Fainelli 633ba0696c2SFlorian Fainelli num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); 634ba0696c2SFlorian Fainelli 635ba0696c2SFlorian Fainelli /* Apply the UDF layout for this filter */ 636ba0696c2SFlorian Fainelli bcm_sf2_cfp_udf_set(priv, layout, slice_num); 637ba0696c2SFlorian Fainelli 638ba0696c2SFlorian Fainelli /* Chained rule, source port match is coming from the rule we are 639ba0696c2SFlorian Fainelli * chained from. 640ba0696c2SFlorian Fainelli */ 641ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_DATA_PORT(7)); 642ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_MASK_PORT(7)); 643ba0696c2SFlorian Fainelli 644ba0696c2SFlorian Fainelli /* 645ba0696c2SFlorian Fainelli * CHAIN ID [31:24] chain to previous slice 646ba0696c2SFlorian Fainelli * Reserved [23:20] 647ba0696c2SFlorian Fainelli * UDF_Valid[11:8] [19:16] 648ba0696c2SFlorian Fainelli * UDF_Valid[7:0] [15:8] 649ba0696c2SFlorian Fainelli * UDF_n_D11 [7:0] 650ba0696c2SFlorian Fainelli */ 651ba0696c2SFlorian Fainelli reg = rule_index[0] << 24 | udf_upper_bits(num_udf) << 16 | 652ba0696c2SFlorian Fainelli udf_lower_bits(num_udf) << 8; 653ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(6)); 654ba0696c2SFlorian Fainelli 655ba0696c2SFlorian Fainelli /* Mask all except chain ID, UDF Valid[8] and UDF Valid[7:0] */ 656ba0696c2SFlorian Fainelli reg = XCESS_ADDR_MASK << 24 | udf_upper_bits(num_udf) << 16 | 657ba0696c2SFlorian Fainelli udf_lower_bits(num_udf) << 8; 658ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); 659ba0696c2SFlorian Fainelli 660ba0696c2SFlorian Fainelli /* Don't care */ 661ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_DATA_PORT(5)); 662ba0696c2SFlorian Fainelli 663ba0696c2SFlorian Fainelli /* Mask all */ 664ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_MASK_PORT(5)); 665ba0696c2SFlorian Fainelli 666ba0696c2SFlorian Fainelli bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6dst, v6_spec->pdst, slice_num); 667ba0696c2SFlorian Fainelli 668ba0696c2SFlorian Fainelli /* Insert into TCAM now */ 669ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, rule_index[1]); 670ba0696c2SFlorian Fainelli 671ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 672ba0696c2SFlorian Fainelli if (ret) { 673ba0696c2SFlorian Fainelli pr_err("TCAM entry at addr %d failed\n", rule_index[1]); 674ba0696c2SFlorian Fainelli goto out_err; 675ba0696c2SFlorian Fainelli } 676ba0696c2SFlorian Fainelli 677ba0696c2SFlorian Fainelli /* Insert into Action and policer RAMs now, set chain ID to 678ba0696c2SFlorian Fainelli * the one we are chained to 679ba0696c2SFlorian Fainelli */ 680ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port_num, 681ba0696c2SFlorian Fainelli queue_num, true); 682ba0696c2SFlorian Fainelli if (ret) 683ba0696c2SFlorian Fainelli goto out_err; 684ba0696c2SFlorian Fainelli 685ba0696c2SFlorian Fainelli /* Turn on CFP for this rule now */ 686ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_CTL_REG); 687ba0696c2SFlorian Fainelli reg |= BIT(port); 688ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_CTL_REG); 689ba0696c2SFlorian Fainelli 690ba0696c2SFlorian Fainelli /* Flag the second half rule as being used now, return it as the 691ba0696c2SFlorian Fainelli * location, and flag it as unique while dumping rules 692ba0696c2SFlorian Fainelli */ 693ba0696c2SFlorian Fainelli set_bit(rule_index[1], priv->cfp.used); 694ba0696c2SFlorian Fainelli set_bit(rule_index[1], priv->cfp.unique); 695ba0696c2SFlorian Fainelli fs->location = rule_index[1]; 696ba0696c2SFlorian Fainelli 697ba0696c2SFlorian Fainelli return ret; 698ba0696c2SFlorian Fainelli 699ba0696c2SFlorian Fainelli out_err: 700ba0696c2SFlorian Fainelli clear_bit(rule_index[0], priv->cfp.used); 701ba0696c2SFlorian Fainelli return ret; 702ba0696c2SFlorian Fainelli } 703ba0696c2SFlorian Fainelli 70433061458SFlorian Fainelli static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, 70533061458SFlorian Fainelli struct ethtool_rx_flow_spec *fs) 70633061458SFlorian Fainelli { 70733061458SFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 70833061458SFlorian Fainelli unsigned int queue_num, port_num; 709ba0696c2SFlorian Fainelli int ret = -EINVAL; 71033061458SFlorian Fainelli 71133061458SFlorian Fainelli /* Check for unsupported extensions */ 71233061458SFlorian Fainelli if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype || 71333061458SFlorian Fainelli fs->m_ext.data[1])) 71433061458SFlorian Fainelli return -EINVAL; 71533061458SFlorian Fainelli 71633061458SFlorian Fainelli if (fs->location != RX_CLS_LOC_ANY && 71733061458SFlorian Fainelli test_bit(fs->location, priv->cfp.used)) 71833061458SFlorian Fainelli return -EBUSY; 71933061458SFlorian Fainelli 72033061458SFlorian Fainelli if (fs->location != RX_CLS_LOC_ANY && 72133061458SFlorian Fainelli fs->location > bcm_sf2_cfp_rule_size(priv)) 72233061458SFlorian Fainelli return -EINVAL; 72333061458SFlorian Fainelli 72433061458SFlorian Fainelli /* We do not support discarding packets, check that the 72533061458SFlorian Fainelli * destination port is enabled and that we are within the 72633061458SFlorian Fainelli * number of ports supported by the switch 72733061458SFlorian Fainelli */ 72833061458SFlorian Fainelli port_num = fs->ring_cookie / SF2_NUM_EGRESS_QUEUES; 72933061458SFlorian Fainelli 73033061458SFlorian Fainelli if (fs->ring_cookie == RX_CLS_FLOW_DISC || 73133061458SFlorian Fainelli !(BIT(port_num) & ds->enabled_port_mask) || 73233061458SFlorian Fainelli port_num >= priv->hw_params.num_ports) 73333061458SFlorian Fainelli return -EINVAL; 73433061458SFlorian Fainelli /* 73533061458SFlorian Fainelli * We have a small oddity where Port 6 just does not have a 73633061458SFlorian Fainelli * valid bit here (so we substract by one). 73733061458SFlorian Fainelli */ 73833061458SFlorian Fainelli queue_num = fs->ring_cookie % SF2_NUM_EGRESS_QUEUES; 73933061458SFlorian Fainelli if (port_num >= 7) 74033061458SFlorian Fainelli port_num -= 1; 74133061458SFlorian Fainelli 742ba0696c2SFlorian Fainelli switch (fs->flow_type & ~FLOW_EXT) { 743ba0696c2SFlorian Fainelli case TCP_V4_FLOW: 744ba0696c2SFlorian Fainelli case UDP_V4_FLOW: 745ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv4_rule_set(priv, port, port_num, 746ba0696c2SFlorian Fainelli queue_num, fs); 747ba0696c2SFlorian Fainelli break; 748ba0696c2SFlorian Fainelli case TCP_V6_FLOW: 749ba0696c2SFlorian Fainelli case UDP_V6_FLOW: 750ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv6_rule_set(priv, port, port_num, 751ba0696c2SFlorian Fainelli queue_num, fs); 752ba0696c2SFlorian Fainelli break; 753ba0696c2SFlorian Fainelli default: 754ba0696c2SFlorian Fainelli break; 75533061458SFlorian Fainelli } 75633061458SFlorian Fainelli 757ba0696c2SFlorian Fainelli return ret; 758ba0696c2SFlorian Fainelli } 759ba0696c2SFlorian Fainelli 760ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_rule_del_one(struct bcm_sf2_priv *priv, int port, 761ba0696c2SFlorian Fainelli u32 loc, u32 *next_loc) 7627318166cSFlorian Fainelli { 7637318166cSFlorian Fainelli int ret; 7647318166cSFlorian Fainelli u32 reg; 7657318166cSFlorian Fainelli 7667318166cSFlorian Fainelli /* Refuse deletion of unused rules, and the default reserved rule */ 7677318166cSFlorian Fainelli if (!test_bit(loc, priv->cfp.used) || loc == 0) 7687318166cSFlorian Fainelli return -EINVAL; 7697318166cSFlorian Fainelli 7707318166cSFlorian Fainelli /* Indicate which rule we want to read */ 7717318166cSFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, loc); 7727318166cSFlorian Fainelli 7737318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); 7747318166cSFlorian Fainelli if (ret) 7757318166cSFlorian Fainelli return ret; 7767318166cSFlorian Fainelli 777ba0696c2SFlorian Fainelli /* Check if this is possibly an IPv6 rule that would 778ba0696c2SFlorian Fainelli * indicate we need to delete its companion rule 779ba0696c2SFlorian Fainelli * as well 780ba0696c2SFlorian Fainelli */ 781ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); 782ba0696c2SFlorian Fainelli if (next_loc) 783ba0696c2SFlorian Fainelli *next_loc = (reg >> 24) & CHAIN_ID_MASK; 784ba0696c2SFlorian Fainelli 7857318166cSFlorian Fainelli /* Clear its valid bits */ 7867318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(0)); 7877318166cSFlorian Fainelli reg &= ~SLICE_VALID; 7887318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(0)); 7897318166cSFlorian Fainelli 7907318166cSFlorian Fainelli /* Write back this entry into the TCAM now */ 7917318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 7927318166cSFlorian Fainelli if (ret) 7937318166cSFlorian Fainelli return ret; 7947318166cSFlorian Fainelli 7957318166cSFlorian Fainelli clear_bit(loc, priv->cfp.used); 796ba0696c2SFlorian Fainelli clear_bit(loc, priv->cfp.unique); 7977318166cSFlorian Fainelli 7987318166cSFlorian Fainelli return 0; 7997318166cSFlorian Fainelli } 8007318166cSFlorian Fainelli 801ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, 802ba0696c2SFlorian Fainelli u32 loc) 803ba0696c2SFlorian Fainelli { 804ba0696c2SFlorian Fainelli u32 next_loc = 0; 805ba0696c2SFlorian Fainelli int ret; 806ba0696c2SFlorian Fainelli 807ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_rule_del_one(priv, port, loc, &next_loc); 808ba0696c2SFlorian Fainelli if (ret) 809ba0696c2SFlorian Fainelli return ret; 810ba0696c2SFlorian Fainelli 811ba0696c2SFlorian Fainelli /* If this was an IPv6 rule, delete is companion rule too */ 812ba0696c2SFlorian Fainelli if (next_loc) 813ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_rule_del_one(priv, port, next_loc, NULL); 814ba0696c2SFlorian Fainelli 815ba0696c2SFlorian Fainelli return ret; 816ba0696c2SFlorian Fainelli } 817ba0696c2SFlorian Fainelli 8187318166cSFlorian Fainelli static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow) 8197318166cSFlorian Fainelli { 8207318166cSFlorian Fainelli unsigned int i; 8217318166cSFlorian Fainelli 8227318166cSFlorian Fainelli for (i = 0; i < sizeof(flow->m_u); i++) 8237318166cSFlorian Fainelli flow->m_u.hdata[i] ^= 0xff; 8247318166cSFlorian Fainelli 8257318166cSFlorian Fainelli flow->m_ext.vlan_etype ^= cpu_to_be16(~0); 8267318166cSFlorian Fainelli flow->m_ext.vlan_tci ^= cpu_to_be16(~0); 8277318166cSFlorian Fainelli flow->m_ext.data[0] ^= cpu_to_be32(~0); 8287318166cSFlorian Fainelli flow->m_ext.data[1] ^= cpu_to_be32(~0); 8297318166cSFlorian Fainelli } 8307318166cSFlorian Fainelli 831bc3fc44cSFlorian Fainelli static int bcm_sf2_cfp_unslice_ipv4(struct bcm_sf2_priv *priv, 832bc3fc44cSFlorian Fainelli struct ethtool_tcpip4_spec *v4_spec, 833bc3fc44cSFlorian Fainelli bool mask) 834bc3fc44cSFlorian Fainelli { 835bc3fc44cSFlorian Fainelli u32 reg, offset, ipv4; 836bc3fc44cSFlorian Fainelli u16 src_dst_port; 837bc3fc44cSFlorian Fainelli 838bc3fc44cSFlorian Fainelli if (mask) 839bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(3); 840bc3fc44cSFlorian Fainelli else 841bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(3); 842bc3fc44cSFlorian Fainelli 843bc3fc44cSFlorian Fainelli reg = core_readl(priv, offset); 844bc3fc44cSFlorian Fainelli /* src port [15:8] */ 845bc3fc44cSFlorian Fainelli src_dst_port = reg << 8; 846bc3fc44cSFlorian Fainelli 847bc3fc44cSFlorian Fainelli if (mask) 848bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(2); 849bc3fc44cSFlorian Fainelli else 850bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(2); 851bc3fc44cSFlorian Fainelli 852bc3fc44cSFlorian Fainelli reg = core_readl(priv, offset); 853bc3fc44cSFlorian Fainelli /* src port [7:0] */ 854bc3fc44cSFlorian Fainelli src_dst_port |= (reg >> 24); 855bc3fc44cSFlorian Fainelli 856bc3fc44cSFlorian Fainelli v4_spec->pdst = cpu_to_be16(src_dst_port); 857bc3fc44cSFlorian Fainelli v4_spec->psrc = cpu_to_be16((u16)(reg >> 8)); 858bc3fc44cSFlorian Fainelli 859bc3fc44cSFlorian Fainelli /* IPv4 dst [15:8] */ 860bc3fc44cSFlorian Fainelli ipv4 = (reg & 0xff) << 8; 861bc3fc44cSFlorian Fainelli 862bc3fc44cSFlorian Fainelli if (mask) 863bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(1); 864bc3fc44cSFlorian Fainelli else 865bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(1); 866bc3fc44cSFlorian Fainelli 867bc3fc44cSFlorian Fainelli reg = core_readl(priv, offset); 868bc3fc44cSFlorian Fainelli /* IPv4 dst [31:16] */ 869bc3fc44cSFlorian Fainelli ipv4 |= ((reg >> 8) & 0xffff) << 16; 870bc3fc44cSFlorian Fainelli /* IPv4 dst [7:0] */ 871bc3fc44cSFlorian Fainelli ipv4 |= (reg >> 24) & 0xff; 872bc3fc44cSFlorian Fainelli v4_spec->ip4dst = cpu_to_be32(ipv4); 873bc3fc44cSFlorian Fainelli 874bc3fc44cSFlorian Fainelli /* IPv4 src [15:8] */ 875bc3fc44cSFlorian Fainelli ipv4 = (reg & 0xff) << 8; 876bc3fc44cSFlorian Fainelli 877bc3fc44cSFlorian Fainelli if (mask) 878bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(0); 879bc3fc44cSFlorian Fainelli else 880bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(0); 881bc3fc44cSFlorian Fainelli reg = core_readl(priv, offset); 882bc3fc44cSFlorian Fainelli 883bc3fc44cSFlorian Fainelli /* Once the TCAM is programmed, the mask reflects the slice number 884bc3fc44cSFlorian Fainelli * being matched, don't bother checking it when reading back the 885bc3fc44cSFlorian Fainelli * mask spec 886bc3fc44cSFlorian Fainelli */ 887bc3fc44cSFlorian Fainelli if (!mask && !(reg & SLICE_VALID)) 888bc3fc44cSFlorian Fainelli return -EINVAL; 889bc3fc44cSFlorian Fainelli 890bc3fc44cSFlorian Fainelli /* IPv4 src [7:0] */ 891bc3fc44cSFlorian Fainelli ipv4 |= (reg >> 24) & 0xff; 892bc3fc44cSFlorian Fainelli /* IPv4 src [31:16] */ 893bc3fc44cSFlorian Fainelli ipv4 |= ((reg >> 8) & 0xffff) << 16; 894bc3fc44cSFlorian Fainelli v4_spec->ip4src = cpu_to_be32(ipv4); 895bc3fc44cSFlorian Fainelli 896bc3fc44cSFlorian Fainelli return 0; 897bc3fc44cSFlorian Fainelli } 898bc3fc44cSFlorian Fainelli 89933061458SFlorian Fainelli static int bcm_sf2_cfp_ipv4_rule_get(struct bcm_sf2_priv *priv, int port, 900ba0696c2SFlorian Fainelli struct ethtool_rx_flow_spec *fs) 90133061458SFlorian Fainelli { 902ba0696c2SFlorian Fainelli struct ethtool_tcpip4_spec *v4_spec = NULL, *v4_m_spec = NULL; 903bc3fc44cSFlorian Fainelli u32 reg; 904bc3fc44cSFlorian Fainelli int ret; 90533061458SFlorian Fainelli 906ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); 907ba0696c2SFlorian Fainelli 908ba0696c2SFlorian Fainelli switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) { 909ba0696c2SFlorian Fainelli case IPPROTO_TCP: 910ba0696c2SFlorian Fainelli fs->flow_type = TCP_V4_FLOW; 911ba0696c2SFlorian Fainelli v4_spec = &fs->h_u.tcp_ip4_spec; 912ba0696c2SFlorian Fainelli v4_m_spec = &fs->m_u.tcp_ip4_spec; 913ba0696c2SFlorian Fainelli break; 914ba0696c2SFlorian Fainelli case IPPROTO_UDP: 915ba0696c2SFlorian Fainelli fs->flow_type = UDP_V4_FLOW; 916ba0696c2SFlorian Fainelli v4_spec = &fs->h_u.udp_ip4_spec; 917ba0696c2SFlorian Fainelli v4_m_spec = &fs->m_u.udp_ip4_spec; 918ba0696c2SFlorian Fainelli break; 919ba0696c2SFlorian Fainelli default: 920ba0696c2SFlorian Fainelli return -EINVAL; 921ba0696c2SFlorian Fainelli } 922ba0696c2SFlorian Fainelli 923ba0696c2SFlorian Fainelli fs->m_ext.data[0] = cpu_to_be32((reg >> IP_FRAG_SHIFT) & 1); 924ba0696c2SFlorian Fainelli v4_spec->tos = (reg >> IPTOS_SHIFT) & IPTOS_MASK; 925ba0696c2SFlorian Fainelli 926bc3fc44cSFlorian Fainelli ret = bcm_sf2_cfp_unslice_ipv4(priv, v4_spec, false); 927bc3fc44cSFlorian Fainelli if (ret) 928bc3fc44cSFlorian Fainelli return ret; 92933061458SFlorian Fainelli 930bc3fc44cSFlorian Fainelli return bcm_sf2_cfp_unslice_ipv4(priv, v4_m_spec, true); 93133061458SFlorian Fainelli } 93233061458SFlorian Fainelli 933ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_unslice_ipv6(struct bcm_sf2_priv *priv, 934ba0696c2SFlorian Fainelli __be32 *ip6_addr, __be16 *port, 935ba0696c2SFlorian Fainelli __be32 *ip6_mask, __be16 *port_mask) 936ba0696c2SFlorian Fainelli { 937ba0696c2SFlorian Fainelli u32 reg, tmp; 938ba0696c2SFlorian Fainelli 939ba0696c2SFlorian Fainelli /* C-Tag [31:24] 940ba0696c2SFlorian Fainelli * UDF_n_B8 [23:8] (port) 941ba0696c2SFlorian Fainelli * UDF_n_B7 (upper) [7:0] (addr[15:8]) 942ba0696c2SFlorian Fainelli */ 943ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(4)); 944ba0696c2SFlorian Fainelli *port = cpu_to_be32(reg) >> 8; 945ba0696c2SFlorian Fainelli *port_mask = cpu_to_be16(~0); 946ba0696c2SFlorian Fainelli tmp = (u32)(reg & 0xff) << 8; 947ba0696c2SFlorian Fainelli 948ba0696c2SFlorian Fainelli /* UDF_n_B7 (lower) [31:24] (addr[7:0]) 949ba0696c2SFlorian Fainelli * UDF_n_B6 [23:8] (addr[31:16]) 950ba0696c2SFlorian Fainelli * UDF_n_B5 (upper) [7:0] (addr[47:40]) 951ba0696c2SFlorian Fainelli */ 952ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(3)); 953ba0696c2SFlorian Fainelli tmp |= (reg >> 24) & 0xff; 954ba0696c2SFlorian Fainelli tmp |= (u32)((reg >> 8) << 16); 955ba0696c2SFlorian Fainelli ip6_mask[3] = cpu_to_be32(~0); 956ba0696c2SFlorian Fainelli ip6_addr[3] = cpu_to_be32(tmp); 957ba0696c2SFlorian Fainelli tmp = (u32)(reg & 0xff) << 8; 958ba0696c2SFlorian Fainelli 959ba0696c2SFlorian Fainelli /* UDF_n_B5 (lower) [31:24] (addr[39:32]) 960ba0696c2SFlorian Fainelli * UDF_n_B4 [23:8] (addr[63:48]) 961ba0696c2SFlorian Fainelli * UDF_n_B3 (upper) [7:0] (addr[79:72]) 962ba0696c2SFlorian Fainelli */ 963ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(2)); 964ba0696c2SFlorian Fainelli tmp |= (reg >> 24) & 0xff; 965ba0696c2SFlorian Fainelli tmp |= (u32)((reg >> 8) << 16); 966ba0696c2SFlorian Fainelli ip6_mask[2] = cpu_to_be32(~0); 967ba0696c2SFlorian Fainelli ip6_addr[2] = cpu_to_be32(tmp); 968ba0696c2SFlorian Fainelli tmp = (u32)(reg & 0xff) << 8; 969ba0696c2SFlorian Fainelli 970ba0696c2SFlorian Fainelli /* UDF_n_B3 (lower) [31:24] (addr[71:64]) 971ba0696c2SFlorian Fainelli * UDF_n_B2 [23:8] (addr[95:80]) 972ba0696c2SFlorian Fainelli * UDF_n_B1 (upper) [7:0] (addr[111:104]) 973ba0696c2SFlorian Fainelli */ 974ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(1)); 975ba0696c2SFlorian Fainelli tmp |= (reg >> 24) & 0xff; 976ba0696c2SFlorian Fainelli tmp |= (u32)((reg >> 8) << 16); 977ba0696c2SFlorian Fainelli ip6_mask[1] = cpu_to_be32(~0); 978ba0696c2SFlorian Fainelli ip6_addr[1] = cpu_to_be32(tmp); 979ba0696c2SFlorian Fainelli tmp = (u32)(reg & 0xff) << 8; 980ba0696c2SFlorian Fainelli 981ba0696c2SFlorian Fainelli /* UDF_n_B1 (lower) [31:24] (addr[103:96]) 982ba0696c2SFlorian Fainelli * UDF_n_B0 [23:8] (addr[127:112]) 983ba0696c2SFlorian Fainelli * Reserved [7:4] 984ba0696c2SFlorian Fainelli * Slice ID [3:2] 985ba0696c2SFlorian Fainelli * Slice valid [1:0] 986ba0696c2SFlorian Fainelli */ 987ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(0)); 988ba0696c2SFlorian Fainelli tmp |= (reg >> 24) & 0xff; 989ba0696c2SFlorian Fainelli tmp |= (u32)((reg >> 8) << 16); 990ba0696c2SFlorian Fainelli ip6_mask[0] = cpu_to_be32(~0); 991ba0696c2SFlorian Fainelli ip6_addr[0] = cpu_to_be32(tmp); 992ba0696c2SFlorian Fainelli 993ba0696c2SFlorian Fainelli if (!(reg & SLICE_VALID)) 994ba0696c2SFlorian Fainelli return -EINVAL; 995ba0696c2SFlorian Fainelli 996ba0696c2SFlorian Fainelli return 0; 997ba0696c2SFlorian Fainelli } 998ba0696c2SFlorian Fainelli 999ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_ipv6_rule_get(struct bcm_sf2_priv *priv, int port, 1000ba0696c2SFlorian Fainelli struct ethtool_rx_flow_spec *fs, 1001ba0696c2SFlorian Fainelli u32 next_loc) 1002ba0696c2SFlorian Fainelli { 1003ba0696c2SFlorian Fainelli struct ethtool_tcpip6_spec *v6_spec = NULL, *v6_m_spec = NULL; 1004ba0696c2SFlorian Fainelli u32 reg; 1005ba0696c2SFlorian Fainelli int ret; 1006ba0696c2SFlorian Fainelli 1007ba0696c2SFlorian Fainelli /* UDPv6 and TCPv6 both use ethtool_tcpip6_spec so we are fine 1008ba0696c2SFlorian Fainelli * assuming tcp_ip6_spec here being an union. 1009ba0696c2SFlorian Fainelli */ 1010ba0696c2SFlorian Fainelli v6_spec = &fs->h_u.tcp_ip6_spec; 1011ba0696c2SFlorian Fainelli v6_m_spec = &fs->m_u.tcp_ip6_spec; 1012ba0696c2SFlorian Fainelli 1013ba0696c2SFlorian Fainelli /* Read the second half first */ 1014ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_spec->ip6dst, &v6_spec->pdst, 1015ba0696c2SFlorian Fainelli v6_m_spec->ip6dst, &v6_m_spec->pdst); 1016ba0696c2SFlorian Fainelli if (ret) 1017ba0696c2SFlorian Fainelli return ret; 1018ba0696c2SFlorian Fainelli 1019ba0696c2SFlorian Fainelli /* Read last to avoid next entry clobbering the results during search 1020ba0696c2SFlorian Fainelli * operations. We would not have the port enabled for this rule, so 1021ba0696c2SFlorian Fainelli * don't bother checking it. 1022ba0696c2SFlorian Fainelli */ 1023ba0696c2SFlorian Fainelli (void)core_readl(priv, CORE_CFP_DATA_PORT(7)); 1024ba0696c2SFlorian Fainelli 1025ba0696c2SFlorian Fainelli /* The slice number is valid, so read the rule we are chained from now 1026ba0696c2SFlorian Fainelli * which is our first half. 1027ba0696c2SFlorian Fainelli */ 1028ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, next_loc); 1029ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); 1030ba0696c2SFlorian Fainelli if (ret) 1031ba0696c2SFlorian Fainelli return ret; 1032ba0696c2SFlorian Fainelli 1033ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); 1034ba0696c2SFlorian Fainelli 1035ba0696c2SFlorian Fainelli switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) { 1036ba0696c2SFlorian Fainelli case IPPROTO_TCP: 1037ba0696c2SFlorian Fainelli fs->flow_type = TCP_V6_FLOW; 1038ba0696c2SFlorian Fainelli break; 1039ba0696c2SFlorian Fainelli case IPPROTO_UDP: 1040ba0696c2SFlorian Fainelli fs->flow_type = UDP_V6_FLOW; 1041ba0696c2SFlorian Fainelli break; 1042ba0696c2SFlorian Fainelli default: 1043ba0696c2SFlorian Fainelli return -EINVAL; 1044ba0696c2SFlorian Fainelli } 1045ba0696c2SFlorian Fainelli 1046ba0696c2SFlorian Fainelli return bcm_sf2_cfp_unslice_ipv6(priv, v6_spec->ip6src, &v6_spec->psrc, 1047ba0696c2SFlorian Fainelli v6_m_spec->ip6src, &v6_m_spec->psrc); 1048ba0696c2SFlorian Fainelli } 1049ba0696c2SFlorian Fainelli 10507318166cSFlorian Fainelli static int bcm_sf2_cfp_rule_get(struct bcm_sf2_priv *priv, int port, 10514daa70cfSFlorian Fainelli struct ethtool_rxnfc *nfc) 10527318166cSFlorian Fainelli { 1053ba0696c2SFlorian Fainelli u32 reg, ipv4_or_chain_id; 10547318166cSFlorian Fainelli unsigned int queue_num; 10557318166cSFlorian Fainelli int ret; 10567318166cSFlorian Fainelli 10577318166cSFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, nfc->fs.location); 10587318166cSFlorian Fainelli 10597318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | ACT_POL_RAM); 10607318166cSFlorian Fainelli if (ret) 10617318166cSFlorian Fainelli return ret; 10627318166cSFlorian Fainelli 10637318166cSFlorian Fainelli reg = core_readl(priv, CORE_ACT_POL_DATA0); 10647318166cSFlorian Fainelli 10657318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); 10667318166cSFlorian Fainelli if (ret) 10677318166cSFlorian Fainelli return ret; 10687318166cSFlorian Fainelli 10697318166cSFlorian Fainelli /* Extract the destination port */ 10707318166cSFlorian Fainelli nfc->fs.ring_cookie = fls((reg >> DST_MAP_IB_SHIFT) & 10717318166cSFlorian Fainelli DST_MAP_IB_MASK) - 1; 10727318166cSFlorian Fainelli 10737318166cSFlorian Fainelli /* There is no Port 6, so we compensate for that here */ 10747318166cSFlorian Fainelli if (nfc->fs.ring_cookie >= 6) 10757318166cSFlorian Fainelli nfc->fs.ring_cookie++; 1076152b6fd6SFlorian Fainelli nfc->fs.ring_cookie *= SF2_NUM_EGRESS_QUEUES; 10777318166cSFlorian Fainelli 10787318166cSFlorian Fainelli /* Extract the destination queue */ 10797318166cSFlorian Fainelli queue_num = (reg >> NEW_TC_SHIFT) & NEW_TC_MASK; 10807318166cSFlorian Fainelli nfc->fs.ring_cookie += queue_num; 10817318166cSFlorian Fainelli 1082ba0696c2SFlorian Fainelli /* Extract the L3_FRAMING or CHAIN_ID */ 10837318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); 10847318166cSFlorian Fainelli 1085ba0696c2SFlorian Fainelli /* With IPv6 rules this would contain a non-zero chain ID since 1086ba0696c2SFlorian Fainelli * we reserve entry 0 and it cannot be used. So if we read 0 here 1087ba0696c2SFlorian Fainelli * this means an IPv4 rule. 1088ba0696c2SFlorian Fainelli */ 1089ba0696c2SFlorian Fainelli ipv4_or_chain_id = (reg >> L3_FRAMING_SHIFT) & 0xff; 1090ba0696c2SFlorian Fainelli if (ipv4_or_chain_id == 0) 1091ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv4_rule_get(priv, port, &nfc->fs); 1092ba0696c2SFlorian Fainelli else 1093ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv6_rule_get(priv, port, &nfc->fs, 1094ba0696c2SFlorian Fainelli ipv4_or_chain_id); 109533061458SFlorian Fainelli if (ret) 109633061458SFlorian Fainelli return ret; 10977318166cSFlorian Fainelli 10987318166cSFlorian Fainelli /* Read last to avoid next entry clobbering the results during search 10997318166cSFlorian Fainelli * operations 11007318166cSFlorian Fainelli */ 11017318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(7)); 11027318166cSFlorian Fainelli if (!(reg & 1 << port)) 11037318166cSFlorian Fainelli return -EINVAL; 11047318166cSFlorian Fainelli 11057318166cSFlorian Fainelli bcm_sf2_invert_masks(&nfc->fs); 11067318166cSFlorian Fainelli 11077318166cSFlorian Fainelli /* Put the TCAM size here */ 11087318166cSFlorian Fainelli nfc->data = bcm_sf2_cfp_rule_size(priv); 11097318166cSFlorian Fainelli 11107318166cSFlorian Fainelli return 0; 11117318166cSFlorian Fainelli } 11127318166cSFlorian Fainelli 11137318166cSFlorian Fainelli /* We implement the search doing a TCAM search operation */ 11147318166cSFlorian Fainelli static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv, 11157318166cSFlorian Fainelli int port, struct ethtool_rxnfc *nfc, 11167318166cSFlorian Fainelli u32 *rule_locs) 11177318166cSFlorian Fainelli { 11187318166cSFlorian Fainelli unsigned int index = 1, rules_cnt = 0; 11197318166cSFlorian Fainelli 1120ba0696c2SFlorian Fainelli for_each_set_bit_from(index, priv->cfp.unique, priv->num_cfp_rules) { 11217318166cSFlorian Fainelli rule_locs[rules_cnt] = index; 11227318166cSFlorian Fainelli rules_cnt++; 11237318166cSFlorian Fainelli } 11247318166cSFlorian Fainelli 11257318166cSFlorian Fainelli /* Put the TCAM size here */ 11267318166cSFlorian Fainelli nfc->data = bcm_sf2_cfp_rule_size(priv); 11277318166cSFlorian Fainelli nfc->rule_cnt = rules_cnt; 11287318166cSFlorian Fainelli 11297318166cSFlorian Fainelli return 0; 11307318166cSFlorian Fainelli } 11317318166cSFlorian Fainelli 11327318166cSFlorian Fainelli int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, 11337318166cSFlorian Fainelli struct ethtool_rxnfc *nfc, u32 *rule_locs) 11347318166cSFlorian Fainelli { 11357318166cSFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 11367318166cSFlorian Fainelli int ret = 0; 11377318166cSFlorian Fainelli 11387318166cSFlorian Fainelli mutex_lock(&priv->cfp.lock); 11397318166cSFlorian Fainelli 11407318166cSFlorian Fainelli switch (nfc->cmd) { 11417318166cSFlorian Fainelli case ETHTOOL_GRXCLSRLCNT: 11427318166cSFlorian Fainelli /* Subtract the default, unusable rule */ 1143ba0696c2SFlorian Fainelli nfc->rule_cnt = bitmap_weight(priv->cfp.unique, 1144df191632SFlorian Fainelli priv->num_cfp_rules) - 1; 11457318166cSFlorian Fainelli /* We support specifying rule locations */ 11467318166cSFlorian Fainelli nfc->data |= RX_CLS_LOC_SPECIAL; 11477318166cSFlorian Fainelli break; 11487318166cSFlorian Fainelli case ETHTOOL_GRXCLSRULE: 11494daa70cfSFlorian Fainelli ret = bcm_sf2_cfp_rule_get(priv, port, nfc); 11507318166cSFlorian Fainelli break; 11517318166cSFlorian Fainelli case ETHTOOL_GRXCLSRLALL: 11527318166cSFlorian Fainelli ret = bcm_sf2_cfp_rule_get_all(priv, port, nfc, rule_locs); 11537318166cSFlorian Fainelli break; 11547318166cSFlorian Fainelli default: 11557318166cSFlorian Fainelli ret = -EOPNOTSUPP; 11567318166cSFlorian Fainelli break; 11577318166cSFlorian Fainelli } 11587318166cSFlorian Fainelli 11597318166cSFlorian Fainelli mutex_unlock(&priv->cfp.lock); 11607318166cSFlorian Fainelli 11617318166cSFlorian Fainelli return ret; 11627318166cSFlorian Fainelli } 11637318166cSFlorian Fainelli 11647318166cSFlorian Fainelli int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, 11657318166cSFlorian Fainelli struct ethtool_rxnfc *nfc) 11667318166cSFlorian Fainelli { 11677318166cSFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 11687318166cSFlorian Fainelli int ret = 0; 11697318166cSFlorian Fainelli 11707318166cSFlorian Fainelli mutex_lock(&priv->cfp.lock); 11717318166cSFlorian Fainelli 11727318166cSFlorian Fainelli switch (nfc->cmd) { 11737318166cSFlorian Fainelli case ETHTOOL_SRXCLSRLINS: 11747318166cSFlorian Fainelli ret = bcm_sf2_cfp_rule_set(ds, port, &nfc->fs); 11757318166cSFlorian Fainelli break; 11767318166cSFlorian Fainelli 11777318166cSFlorian Fainelli case ETHTOOL_SRXCLSRLDEL: 11787318166cSFlorian Fainelli ret = bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location); 11797318166cSFlorian Fainelli break; 11807318166cSFlorian Fainelli default: 11817318166cSFlorian Fainelli ret = -EOPNOTSUPP; 11827318166cSFlorian Fainelli break; 11837318166cSFlorian Fainelli } 11847318166cSFlorian Fainelli 11857318166cSFlorian Fainelli mutex_unlock(&priv->cfp.lock); 11867318166cSFlorian Fainelli 11877318166cSFlorian Fainelli return ret; 11887318166cSFlorian Fainelli } 11897318166cSFlorian Fainelli 11907318166cSFlorian Fainelli int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv) 11917318166cSFlorian Fainelli { 11927318166cSFlorian Fainelli unsigned int timeout = 1000; 11937318166cSFlorian Fainelli u32 reg; 11947318166cSFlorian Fainelli 11957318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 11967318166cSFlorian Fainelli reg |= TCAM_RESET; 11977318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_ACC); 11987318166cSFlorian Fainelli 11997318166cSFlorian Fainelli do { 12007318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 12017318166cSFlorian Fainelli if (!(reg & TCAM_RESET)) 12027318166cSFlorian Fainelli break; 12037318166cSFlorian Fainelli 12047318166cSFlorian Fainelli cpu_relax(); 12057318166cSFlorian Fainelli } while (timeout--); 12067318166cSFlorian Fainelli 12077318166cSFlorian Fainelli if (!timeout) 12087318166cSFlorian Fainelli return -ETIMEDOUT; 12097318166cSFlorian Fainelli 12107318166cSFlorian Fainelli return 0; 12117318166cSFlorian Fainelli } 1212