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, 35743a5e00fSFlorian Fainelli priv->num_cfp_rules); 35833061458SFlorian Fainelli else 35933061458SFlorian Fainelli rule_index = fs->location; 36033061458SFlorian Fainelli 36143a5e00fSFlorian Fainelli if (rule_index > bcm_sf2_cfp_rule_size(priv)) 36243a5e00fSFlorian Fainelli return -ENOSPC; 36343a5e00fSFlorian Fainelli 3647318166cSFlorian Fainelli layout = &udf_tcpip4_layout; 3655d80bcbbSFlorian Fainelli /* We only use one UDF slice for now */ 3665d80bcbbSFlorian Fainelli slice_num = bcm_sf2_get_slice_number(layout, 0); 3675d80bcbbSFlorian Fainelli if (slice_num == UDF_NUM_SLICES) 3685d80bcbbSFlorian Fainelli return -EINVAL; 3695d80bcbbSFlorian Fainelli 3705d80bcbbSFlorian Fainelli num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); 3717318166cSFlorian Fainelli 3727318166cSFlorian Fainelli /* Apply the UDF layout for this filter */ 3735d80bcbbSFlorian Fainelli bcm_sf2_cfp_udf_set(priv, layout, slice_num); 3747318166cSFlorian Fainelli 3757318166cSFlorian Fainelli /* Apply to all packets received through this port */ 3767318166cSFlorian Fainelli core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7)); 3777318166cSFlorian Fainelli 37833061458SFlorian Fainelli /* Source port map match */ 37933061458SFlorian Fainelli core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7)); 38033061458SFlorian Fainelli 3817318166cSFlorian Fainelli /* S-Tag status [31:30] 3827318166cSFlorian Fainelli * C-Tag status [29:28] 3837318166cSFlorian Fainelli * L2 framing [27:26] 3847318166cSFlorian Fainelli * L3 framing [25:24] 3857318166cSFlorian Fainelli * IP ToS [23:16] 3867318166cSFlorian Fainelli * IP proto [15:08] 3877318166cSFlorian Fainelli * IP Fragm [7] 3887318166cSFlorian Fainelli * Non 1st frag [6] 3897318166cSFlorian Fainelli * IP Authen [5] 3907318166cSFlorian Fainelli * TTL range [4:3] 3917318166cSFlorian Fainelli * PPPoE session [2] 3927318166cSFlorian Fainelli * Reserved [1] 3937318166cSFlorian Fainelli * UDF_Valid[8] [0] 3947318166cSFlorian Fainelli */ 39539cdd349SFlorian Fainelli core_writel(priv, v4_spec->tos << IPTOS_SHIFT | 3965d80bcbbSFlorian Fainelli ip_proto << IPPROTO_SHIFT | ip_frag << IP_FRAG_SHIFT | 3975d80bcbbSFlorian Fainelli udf_upper_bits(num_udf), 3987318166cSFlorian Fainelli CORE_CFP_DATA_PORT(6)); 3997318166cSFlorian Fainelli 400bc3fc44cSFlorian Fainelli /* Mask with the specific layout for IPv4 packets */ 401bc3fc44cSFlorian Fainelli core_writel(priv, layout->udfs[slice_num].mask_value | 402bc3fc44cSFlorian Fainelli udf_upper_bits(num_udf), CORE_CFP_MASK_PORT(6)); 403bc3fc44cSFlorian Fainelli 4047318166cSFlorian Fainelli /* UDF_Valid[7:0] [31:24] 4057318166cSFlorian Fainelli * S-Tag [23:8] 4067318166cSFlorian Fainelli * C-Tag [7:0] 4077318166cSFlorian Fainelli */ 4085d80bcbbSFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); 4097318166cSFlorian Fainelli 4107318166cSFlorian Fainelli /* Mask all but valid UDFs */ 4115d80bcbbSFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); 4127318166cSFlorian Fainelli 413bc3fc44cSFlorian Fainelli /* Program the match and the mask */ 414bc3fc44cSFlorian Fainelli bcm_sf2_cfp_slice_ipv4(priv, v4_spec, slice_num, false); 415bc3fc44cSFlorian Fainelli bcm_sf2_cfp_slice_ipv4(priv, v4_m_spec, SLICE_NUM_MASK, true); 4167318166cSFlorian Fainelli 4177318166cSFlorian Fainelli /* Insert into TCAM now */ 4187318166cSFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, rule_index); 4197318166cSFlorian Fainelli 4207318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 4217318166cSFlorian Fainelli if (ret) { 4227318166cSFlorian Fainelli pr_err("TCAM entry at addr %d failed\n", rule_index); 4237318166cSFlorian Fainelli return ret; 4247318166cSFlorian Fainelli } 4257318166cSFlorian Fainelli 42633061458SFlorian Fainelli /* Insert into Action and policer RAMs now */ 427ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_act_pol_set(priv, rule_index, port_num, 428ba0696c2SFlorian Fainelli queue_num, true); 42933061458SFlorian Fainelli if (ret) 4307318166cSFlorian Fainelli return ret; 4317318166cSFlorian Fainelli 4327318166cSFlorian Fainelli /* Turn on CFP for this rule now */ 4337318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_CTL_REG); 4347318166cSFlorian Fainelli reg |= BIT(port); 4357318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_CTL_REG); 4367318166cSFlorian Fainelli 4377318166cSFlorian Fainelli /* Flag the rule as being used and return it */ 4387318166cSFlorian Fainelli set_bit(rule_index, priv->cfp.used); 439ba0696c2SFlorian Fainelli set_bit(rule_index, priv->cfp.unique); 4407318166cSFlorian Fainelli fs->location = rule_index; 4417318166cSFlorian Fainelli 4427318166cSFlorian Fainelli return 0; 4437318166cSFlorian Fainelli } 4447318166cSFlorian Fainelli 445ba0696c2SFlorian Fainelli static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv, 446ba0696c2SFlorian Fainelli const __be32 *ip6_addr, const __be16 port, 447dd8eff68SFlorian Fainelli unsigned int slice_num, 448dd8eff68SFlorian Fainelli bool mask) 449ba0696c2SFlorian Fainelli { 450dd8eff68SFlorian Fainelli u32 reg, tmp, val, offset; 451ba0696c2SFlorian Fainelli 452ba0696c2SFlorian Fainelli /* C-Tag [31:24] 453ba0696c2SFlorian Fainelli * UDF_n_B8 [23:8] (port) 454ba0696c2SFlorian Fainelli * UDF_n_B7 (upper) [7:0] (addr[15:8]) 455ba0696c2SFlorian Fainelli */ 456ba0696c2SFlorian Fainelli reg = be32_to_cpu(ip6_addr[3]); 457ba0696c2SFlorian Fainelli val = (u32)be16_to_cpu(port) << 8 | ((reg >> 8) & 0xff); 458dd8eff68SFlorian Fainelli if (mask) 459dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(4); 460dd8eff68SFlorian Fainelli else 461dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(4); 462dd8eff68SFlorian Fainelli core_writel(priv, val, offset); 463ba0696c2SFlorian Fainelli 464ba0696c2SFlorian Fainelli /* UDF_n_B7 (lower) [31:24] (addr[7:0]) 465ba0696c2SFlorian Fainelli * UDF_n_B6 [23:8] (addr[31:16]) 466ba0696c2SFlorian Fainelli * UDF_n_B5 (upper) [7:0] (addr[47:40]) 467ba0696c2SFlorian Fainelli */ 468ba0696c2SFlorian Fainelli tmp = be32_to_cpu(ip6_addr[2]); 469ba0696c2SFlorian Fainelli val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 | 470ba0696c2SFlorian Fainelli ((tmp >> 8) & 0xff); 471dd8eff68SFlorian Fainelli if (mask) 472dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(3); 473dd8eff68SFlorian Fainelli else 474dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(3); 475dd8eff68SFlorian Fainelli core_writel(priv, val, offset); 476ba0696c2SFlorian Fainelli 477ba0696c2SFlorian Fainelli /* UDF_n_B5 (lower) [31:24] (addr[39:32]) 478ba0696c2SFlorian Fainelli * UDF_n_B4 [23:8] (addr[63:48]) 479ba0696c2SFlorian Fainelli * UDF_n_B3 (upper) [7:0] (addr[79:72]) 480ba0696c2SFlorian Fainelli */ 481ba0696c2SFlorian Fainelli reg = be32_to_cpu(ip6_addr[1]); 482ba0696c2SFlorian Fainelli val = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 | 483ba0696c2SFlorian Fainelli ((reg >> 8) & 0xff); 484dd8eff68SFlorian Fainelli if (mask) 485dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(2); 486dd8eff68SFlorian Fainelli else 487dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(2); 488dd8eff68SFlorian Fainelli core_writel(priv, val, offset); 489ba0696c2SFlorian Fainelli 490ba0696c2SFlorian Fainelli /* UDF_n_B3 (lower) [31:24] (addr[71:64]) 491ba0696c2SFlorian Fainelli * UDF_n_B2 [23:8] (addr[95:80]) 492ba0696c2SFlorian Fainelli * UDF_n_B1 (upper) [7:0] (addr[111:104]) 493ba0696c2SFlorian Fainelli */ 494ba0696c2SFlorian Fainelli tmp = be32_to_cpu(ip6_addr[0]); 495ba0696c2SFlorian Fainelli val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 | 496ba0696c2SFlorian Fainelli ((tmp >> 8) & 0xff); 497dd8eff68SFlorian Fainelli if (mask) 498dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(1); 499dd8eff68SFlorian Fainelli else 500dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(1); 501dd8eff68SFlorian Fainelli core_writel(priv, val, offset); 502ba0696c2SFlorian Fainelli 503ba0696c2SFlorian Fainelli /* UDF_n_B1 (lower) [31:24] (addr[103:96]) 504ba0696c2SFlorian Fainelli * UDF_n_B0 [23:8] (addr[127:112]) 505ba0696c2SFlorian Fainelli * Reserved [7:4] 506ba0696c2SFlorian Fainelli * Slice ID [3:2] 507ba0696c2SFlorian Fainelli * Slice valid [1:0] 508ba0696c2SFlorian Fainelli */ 509ba0696c2SFlorian Fainelli reg = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 | 510ba0696c2SFlorian Fainelli SLICE_NUM(slice_num) | SLICE_VALID; 511dd8eff68SFlorian Fainelli if (mask) 512dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(0); 513dd8eff68SFlorian Fainelli else 514dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(0); 515dd8eff68SFlorian Fainelli core_writel(priv, reg, offset); 516ba0696c2SFlorian Fainelli } 517ba0696c2SFlorian Fainelli 518ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, 519ba0696c2SFlorian Fainelli unsigned int port_num, 520ba0696c2SFlorian Fainelli unsigned int queue_num, 521ba0696c2SFlorian Fainelli struct ethtool_rx_flow_spec *fs) 522ba0696c2SFlorian Fainelli { 523dd8eff68SFlorian Fainelli struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec; 524ba0696c2SFlorian Fainelli unsigned int slice_num, rule_index[2]; 525ba0696c2SFlorian Fainelli const struct cfp_udf_layout *layout; 526ba0696c2SFlorian Fainelli u8 ip_proto, ip_frag; 527ba0696c2SFlorian Fainelli int ret = 0; 528ba0696c2SFlorian Fainelli u8 num_udf; 529ba0696c2SFlorian Fainelli u32 reg; 530ba0696c2SFlorian Fainelli 531ba0696c2SFlorian Fainelli switch (fs->flow_type & ~FLOW_EXT) { 532ba0696c2SFlorian Fainelli case TCP_V6_FLOW: 533ba0696c2SFlorian Fainelli ip_proto = IPPROTO_TCP; 534ba0696c2SFlorian Fainelli v6_spec = &fs->h_u.tcp_ip6_spec; 535dd8eff68SFlorian Fainelli v6_m_spec = &fs->m_u.tcp_ip6_spec; 536ba0696c2SFlorian Fainelli break; 537ba0696c2SFlorian Fainelli case UDP_V6_FLOW: 538ba0696c2SFlorian Fainelli ip_proto = IPPROTO_UDP; 539ba0696c2SFlorian Fainelli v6_spec = &fs->h_u.udp_ip6_spec; 540dd8eff68SFlorian Fainelli v6_m_spec = &fs->m_u.udp_ip6_spec; 541ba0696c2SFlorian Fainelli break; 542ba0696c2SFlorian Fainelli default: 543ba0696c2SFlorian Fainelli return -EINVAL; 544ba0696c2SFlorian Fainelli } 545ba0696c2SFlorian Fainelli 546ba0696c2SFlorian Fainelli ip_frag = be32_to_cpu(fs->m_ext.data[0]); 547ba0696c2SFlorian Fainelli 548ba0696c2SFlorian Fainelli layout = &udf_tcpip6_layout; 549ba0696c2SFlorian Fainelli slice_num = bcm_sf2_get_slice_number(layout, 0); 550ba0696c2SFlorian Fainelli if (slice_num == UDF_NUM_SLICES) 551ba0696c2SFlorian Fainelli return -EINVAL; 552ba0696c2SFlorian Fainelli 553ba0696c2SFlorian Fainelli num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); 554ba0696c2SFlorian Fainelli 555ba0696c2SFlorian Fainelli /* Negotiate two indexes, one for the second half which we are chained 556ba0696c2SFlorian Fainelli * from, which is what we will return to user-space, and a second one 557ba0696c2SFlorian Fainelli * which is used to store its first half. That first half does not 558ba0696c2SFlorian Fainelli * allow any choice of placement, so it just needs to find the next 559ba0696c2SFlorian Fainelli * available bit. We return the second half as fs->location because 560ba0696c2SFlorian Fainelli * that helps with the rule lookup later on since the second half is 561ba0696c2SFlorian Fainelli * chained from its first half, we can easily identify IPv6 CFP rules 562ba0696c2SFlorian Fainelli * by looking whether they carry a CHAIN_ID. 563ba0696c2SFlorian Fainelli * 564ba0696c2SFlorian Fainelli * We also want the second half to have a lower rule_index than its 565ba0696c2SFlorian Fainelli * first half because the HW search is by incrementing addresses. 566ba0696c2SFlorian Fainelli */ 567ba0696c2SFlorian Fainelli if (fs->location == RX_CLS_LOC_ANY) 568ba0696c2SFlorian Fainelli rule_index[0] = find_first_zero_bit(priv->cfp.used, 569ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_size(priv)); 570ba0696c2SFlorian Fainelli else 571ba0696c2SFlorian Fainelli rule_index[0] = fs->location; 572ba0696c2SFlorian Fainelli 573ba0696c2SFlorian Fainelli /* Flag it as used (cleared on error path) such that we can immediately 574ba0696c2SFlorian Fainelli * obtain a second one to chain from. 575ba0696c2SFlorian Fainelli */ 576ba0696c2SFlorian Fainelli set_bit(rule_index[0], priv->cfp.used); 577ba0696c2SFlorian Fainelli 578ba0696c2SFlorian Fainelli rule_index[1] = find_first_zero_bit(priv->cfp.used, 579ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_size(priv)); 580ba0696c2SFlorian Fainelli if (rule_index[1] > bcm_sf2_cfp_rule_size(priv)) { 581ba0696c2SFlorian Fainelli ret = -ENOSPC; 582ba0696c2SFlorian Fainelli goto out_err; 583ba0696c2SFlorian Fainelli } 584ba0696c2SFlorian Fainelli 585ba0696c2SFlorian Fainelli /* Apply the UDF layout for this filter */ 586ba0696c2SFlorian Fainelli bcm_sf2_cfp_udf_set(priv, layout, slice_num); 587ba0696c2SFlorian Fainelli 588ba0696c2SFlorian Fainelli /* Apply to all packets received through this port */ 589ba0696c2SFlorian Fainelli core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7)); 590ba0696c2SFlorian Fainelli 591ba0696c2SFlorian Fainelli /* Source port map match */ 592ba0696c2SFlorian Fainelli core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7)); 593ba0696c2SFlorian Fainelli 594ba0696c2SFlorian Fainelli /* S-Tag status [31:30] 595ba0696c2SFlorian Fainelli * C-Tag status [29:28] 596ba0696c2SFlorian Fainelli * L2 framing [27:26] 597ba0696c2SFlorian Fainelli * L3 framing [25:24] 598ba0696c2SFlorian Fainelli * IP ToS [23:16] 599ba0696c2SFlorian Fainelli * IP proto [15:08] 600ba0696c2SFlorian Fainelli * IP Fragm [7] 601ba0696c2SFlorian Fainelli * Non 1st frag [6] 602ba0696c2SFlorian Fainelli * IP Authen [5] 603ba0696c2SFlorian Fainelli * TTL range [4:3] 604ba0696c2SFlorian Fainelli * PPPoE session [2] 605ba0696c2SFlorian Fainelli * Reserved [1] 606ba0696c2SFlorian Fainelli * UDF_Valid[8] [0] 607ba0696c2SFlorian Fainelli */ 608ba0696c2SFlorian Fainelli reg = 1 << L3_FRAMING_SHIFT | ip_proto << IPPROTO_SHIFT | 609ba0696c2SFlorian Fainelli ip_frag << IP_FRAG_SHIFT | udf_upper_bits(num_udf); 610ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(6)); 611ba0696c2SFlorian Fainelli 612ba0696c2SFlorian Fainelli /* Mask with the specific layout for IPv6 packets including 613ba0696c2SFlorian Fainelli * UDF_Valid[8] 614ba0696c2SFlorian Fainelli */ 615ba0696c2SFlorian Fainelli reg = layout->udfs[slice_num].mask_value | udf_upper_bits(num_udf); 616ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); 617ba0696c2SFlorian Fainelli 618ba0696c2SFlorian Fainelli /* UDF_Valid[7:0] [31:24] 619ba0696c2SFlorian Fainelli * S-Tag [23:8] 620ba0696c2SFlorian Fainelli * C-Tag [7:0] 621ba0696c2SFlorian Fainelli */ 622ba0696c2SFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); 623ba0696c2SFlorian Fainelli 624ba0696c2SFlorian Fainelli /* Mask all but valid UDFs */ 625ba0696c2SFlorian Fainelli core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); 626ba0696c2SFlorian Fainelli 627ba0696c2SFlorian Fainelli /* Slice the IPv6 source address and port */ 628dd8eff68SFlorian Fainelli bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6src, v6_spec->psrc, 629dd8eff68SFlorian Fainelli slice_num, false); 630dd8eff68SFlorian Fainelli bcm_sf2_cfp_slice_ipv6(priv, v6_m_spec->ip6src, v6_m_spec->psrc, 6316fef90c6SFlorian Fainelli SLICE_NUM_MASK, true); 632ba0696c2SFlorian Fainelli 633ba0696c2SFlorian Fainelli /* Insert into TCAM now because we need to insert a second rule */ 634ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, rule_index[0]); 635ba0696c2SFlorian Fainelli 636ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 637ba0696c2SFlorian Fainelli if (ret) { 638ba0696c2SFlorian Fainelli pr_err("TCAM entry at addr %d failed\n", rule_index[0]); 639ba0696c2SFlorian Fainelli goto out_err; 640ba0696c2SFlorian Fainelli } 641ba0696c2SFlorian Fainelli 642ba0696c2SFlorian Fainelli /* Insert into Action and policer RAMs now */ 643ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port_num, 644ba0696c2SFlorian Fainelli queue_num, false); 645ba0696c2SFlorian Fainelli if (ret) 646ba0696c2SFlorian Fainelli goto out_err; 647ba0696c2SFlorian Fainelli 648ba0696c2SFlorian Fainelli /* Now deal with the second slice to chain this rule */ 649ba0696c2SFlorian Fainelli slice_num = bcm_sf2_get_slice_number(layout, slice_num + 1); 650ba0696c2SFlorian Fainelli if (slice_num == UDF_NUM_SLICES) { 651ba0696c2SFlorian Fainelli ret = -EINVAL; 652ba0696c2SFlorian Fainelli goto out_err; 653ba0696c2SFlorian Fainelli } 654ba0696c2SFlorian Fainelli 655ba0696c2SFlorian Fainelli num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices); 656ba0696c2SFlorian Fainelli 657ba0696c2SFlorian Fainelli /* Apply the UDF layout for this filter */ 658ba0696c2SFlorian Fainelli bcm_sf2_cfp_udf_set(priv, layout, slice_num); 659ba0696c2SFlorian Fainelli 660ba0696c2SFlorian Fainelli /* Chained rule, source port match is coming from the rule we are 661ba0696c2SFlorian Fainelli * chained from. 662ba0696c2SFlorian Fainelli */ 663ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_DATA_PORT(7)); 664ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_MASK_PORT(7)); 665ba0696c2SFlorian Fainelli 666ba0696c2SFlorian Fainelli /* 667ba0696c2SFlorian Fainelli * CHAIN ID [31:24] chain to previous slice 668ba0696c2SFlorian Fainelli * Reserved [23:20] 669ba0696c2SFlorian Fainelli * UDF_Valid[11:8] [19:16] 670ba0696c2SFlorian Fainelli * UDF_Valid[7:0] [15:8] 671ba0696c2SFlorian Fainelli * UDF_n_D11 [7:0] 672ba0696c2SFlorian Fainelli */ 673ba0696c2SFlorian Fainelli reg = rule_index[0] << 24 | udf_upper_bits(num_udf) << 16 | 674ba0696c2SFlorian Fainelli udf_lower_bits(num_udf) << 8; 675ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(6)); 676ba0696c2SFlorian Fainelli 677ba0696c2SFlorian Fainelli /* Mask all except chain ID, UDF Valid[8] and UDF Valid[7:0] */ 678ba0696c2SFlorian Fainelli reg = XCESS_ADDR_MASK << 24 | udf_upper_bits(num_udf) << 16 | 679ba0696c2SFlorian Fainelli udf_lower_bits(num_udf) << 8; 680ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); 681ba0696c2SFlorian Fainelli 682ba0696c2SFlorian Fainelli /* Don't care */ 683ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_DATA_PORT(5)); 684ba0696c2SFlorian Fainelli 685ba0696c2SFlorian Fainelli /* Mask all */ 686ba0696c2SFlorian Fainelli core_writel(priv, 0, CORE_CFP_MASK_PORT(5)); 687ba0696c2SFlorian Fainelli 688dd8eff68SFlorian Fainelli bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6dst, v6_spec->pdst, slice_num, 689dd8eff68SFlorian Fainelli false); 690dd8eff68SFlorian Fainelli bcm_sf2_cfp_slice_ipv6(priv, v6_m_spec->ip6dst, v6_m_spec->pdst, 691dd8eff68SFlorian Fainelli SLICE_NUM_MASK, true); 692ba0696c2SFlorian Fainelli 693ba0696c2SFlorian Fainelli /* Insert into TCAM now */ 694ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, rule_index[1]); 695ba0696c2SFlorian Fainelli 696ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 697ba0696c2SFlorian Fainelli if (ret) { 698ba0696c2SFlorian Fainelli pr_err("TCAM entry at addr %d failed\n", rule_index[1]); 699ba0696c2SFlorian Fainelli goto out_err; 700ba0696c2SFlorian Fainelli } 701ba0696c2SFlorian Fainelli 702ba0696c2SFlorian Fainelli /* Insert into Action and policer RAMs now, set chain ID to 703ba0696c2SFlorian Fainelli * the one we are chained to 704ba0696c2SFlorian Fainelli */ 7056fef90c6SFlorian Fainelli ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[1], port_num, 706ba0696c2SFlorian Fainelli queue_num, true); 707ba0696c2SFlorian Fainelli if (ret) 708ba0696c2SFlorian Fainelli goto out_err; 709ba0696c2SFlorian Fainelli 710ba0696c2SFlorian Fainelli /* Turn on CFP for this rule now */ 711ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_CTL_REG); 712ba0696c2SFlorian Fainelli reg |= BIT(port); 713ba0696c2SFlorian Fainelli core_writel(priv, reg, CORE_CFP_CTL_REG); 714ba0696c2SFlorian Fainelli 715ba0696c2SFlorian Fainelli /* Flag the second half rule as being used now, return it as the 716ba0696c2SFlorian Fainelli * location, and flag it as unique while dumping rules 717ba0696c2SFlorian Fainelli */ 718ba0696c2SFlorian Fainelli set_bit(rule_index[1], priv->cfp.used); 719ba0696c2SFlorian Fainelli set_bit(rule_index[1], priv->cfp.unique); 720ba0696c2SFlorian Fainelli fs->location = rule_index[1]; 721ba0696c2SFlorian Fainelli 722ba0696c2SFlorian Fainelli return ret; 723ba0696c2SFlorian Fainelli 724ba0696c2SFlorian Fainelli out_err: 725ba0696c2SFlorian Fainelli clear_bit(rule_index[0], priv->cfp.used); 726ba0696c2SFlorian Fainelli return ret; 727ba0696c2SFlorian Fainelli } 728ba0696c2SFlorian Fainelli 72933061458SFlorian Fainelli static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, 73033061458SFlorian Fainelli struct ethtool_rx_flow_spec *fs) 73133061458SFlorian Fainelli { 73233061458SFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 73333061458SFlorian Fainelli unsigned int queue_num, port_num; 734ba0696c2SFlorian Fainelli int ret = -EINVAL; 73533061458SFlorian Fainelli 73633061458SFlorian Fainelli /* Check for unsupported extensions */ 73733061458SFlorian Fainelli if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype || 73833061458SFlorian Fainelli fs->m_ext.data[1])) 73933061458SFlorian Fainelli return -EINVAL; 74033061458SFlorian Fainelli 74133061458SFlorian Fainelli if (fs->location != RX_CLS_LOC_ANY && 74233061458SFlorian Fainelli test_bit(fs->location, priv->cfp.used)) 74333061458SFlorian Fainelli return -EBUSY; 74433061458SFlorian Fainelli 74533061458SFlorian Fainelli if (fs->location != RX_CLS_LOC_ANY && 74633061458SFlorian Fainelli fs->location > bcm_sf2_cfp_rule_size(priv)) 74733061458SFlorian Fainelli return -EINVAL; 74833061458SFlorian Fainelli 74933061458SFlorian Fainelli /* We do not support discarding packets, check that the 75033061458SFlorian Fainelli * destination port is enabled and that we are within the 75133061458SFlorian Fainelli * number of ports supported by the switch 75233061458SFlorian Fainelli */ 75333061458SFlorian Fainelli port_num = fs->ring_cookie / SF2_NUM_EGRESS_QUEUES; 75433061458SFlorian Fainelli 75533061458SFlorian Fainelli if (fs->ring_cookie == RX_CLS_FLOW_DISC || 7564a5b85ffSVivien Didelot !dsa_is_user_port(ds, port_num) || 75733061458SFlorian Fainelli port_num >= priv->hw_params.num_ports) 75833061458SFlorian Fainelli return -EINVAL; 75933061458SFlorian Fainelli /* 76033061458SFlorian Fainelli * We have a small oddity where Port 6 just does not have a 76133061458SFlorian Fainelli * valid bit here (so we substract by one). 76233061458SFlorian Fainelli */ 76333061458SFlorian Fainelli queue_num = fs->ring_cookie % SF2_NUM_EGRESS_QUEUES; 76433061458SFlorian Fainelli if (port_num >= 7) 76533061458SFlorian Fainelli port_num -= 1; 76633061458SFlorian Fainelli 767ba0696c2SFlorian Fainelli switch (fs->flow_type & ~FLOW_EXT) { 768ba0696c2SFlorian Fainelli case TCP_V4_FLOW: 769ba0696c2SFlorian Fainelli case UDP_V4_FLOW: 770ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv4_rule_set(priv, port, port_num, 771ba0696c2SFlorian Fainelli queue_num, fs); 772ba0696c2SFlorian Fainelli break; 773ba0696c2SFlorian Fainelli case TCP_V6_FLOW: 774ba0696c2SFlorian Fainelli case UDP_V6_FLOW: 775ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv6_rule_set(priv, port, port_num, 776ba0696c2SFlorian Fainelli queue_num, fs); 777ba0696c2SFlorian Fainelli break; 778ba0696c2SFlorian Fainelli default: 779ba0696c2SFlorian Fainelli break; 78033061458SFlorian Fainelli } 78133061458SFlorian Fainelli 782ba0696c2SFlorian Fainelli return ret; 783ba0696c2SFlorian Fainelli } 784ba0696c2SFlorian Fainelli 785ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_rule_del_one(struct bcm_sf2_priv *priv, int port, 786ba0696c2SFlorian Fainelli u32 loc, u32 *next_loc) 7877318166cSFlorian Fainelli { 7887318166cSFlorian Fainelli int ret; 7897318166cSFlorian Fainelli u32 reg; 7907318166cSFlorian Fainelli 7917318166cSFlorian Fainelli /* Refuse deletion of unused rules, and the default reserved rule */ 7927318166cSFlorian Fainelli if (!test_bit(loc, priv->cfp.used) || loc == 0) 7937318166cSFlorian Fainelli return -EINVAL; 7947318166cSFlorian Fainelli 7957318166cSFlorian Fainelli /* Indicate which rule we want to read */ 7967318166cSFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, loc); 7977318166cSFlorian Fainelli 7987318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); 7997318166cSFlorian Fainelli if (ret) 8007318166cSFlorian Fainelli return ret; 8017318166cSFlorian Fainelli 802ba0696c2SFlorian Fainelli /* Check if this is possibly an IPv6 rule that would 803ba0696c2SFlorian Fainelli * indicate we need to delete its companion rule 804ba0696c2SFlorian Fainelli * as well 805ba0696c2SFlorian Fainelli */ 806ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); 807ba0696c2SFlorian Fainelli if (next_loc) 808ba0696c2SFlorian Fainelli *next_loc = (reg >> 24) & CHAIN_ID_MASK; 809ba0696c2SFlorian Fainelli 8107318166cSFlorian Fainelli /* Clear its valid bits */ 8117318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(0)); 8127318166cSFlorian Fainelli reg &= ~SLICE_VALID; 8137318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_DATA_PORT(0)); 8147318166cSFlorian Fainelli 8157318166cSFlorian Fainelli /* Write back this entry into the TCAM now */ 8167318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL); 8177318166cSFlorian Fainelli if (ret) 8187318166cSFlorian Fainelli return ret; 8197318166cSFlorian Fainelli 8207318166cSFlorian Fainelli clear_bit(loc, priv->cfp.used); 821ba0696c2SFlorian Fainelli clear_bit(loc, priv->cfp.unique); 8227318166cSFlorian Fainelli 8237318166cSFlorian Fainelli return 0; 8247318166cSFlorian Fainelli } 8257318166cSFlorian Fainelli 826ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, 827ba0696c2SFlorian Fainelli u32 loc) 828ba0696c2SFlorian Fainelli { 829ba0696c2SFlorian Fainelli u32 next_loc = 0; 830ba0696c2SFlorian Fainelli int ret; 831ba0696c2SFlorian Fainelli 832ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_rule_del_one(priv, port, loc, &next_loc); 833ba0696c2SFlorian Fainelli if (ret) 834ba0696c2SFlorian Fainelli return ret; 835ba0696c2SFlorian Fainelli 836ba0696c2SFlorian Fainelli /* If this was an IPv6 rule, delete is companion rule too */ 837ba0696c2SFlorian Fainelli if (next_loc) 838ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_rule_del_one(priv, port, next_loc, NULL); 839ba0696c2SFlorian Fainelli 840ba0696c2SFlorian Fainelli return ret; 841ba0696c2SFlorian Fainelli } 842ba0696c2SFlorian Fainelli 8437318166cSFlorian Fainelli static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow) 8447318166cSFlorian Fainelli { 8457318166cSFlorian Fainelli unsigned int i; 8467318166cSFlorian Fainelli 8477318166cSFlorian Fainelli for (i = 0; i < sizeof(flow->m_u); i++) 8487318166cSFlorian Fainelli flow->m_u.hdata[i] ^= 0xff; 8497318166cSFlorian Fainelli 8507318166cSFlorian Fainelli flow->m_ext.vlan_etype ^= cpu_to_be16(~0); 8517318166cSFlorian Fainelli flow->m_ext.vlan_tci ^= cpu_to_be16(~0); 8527318166cSFlorian Fainelli flow->m_ext.data[0] ^= cpu_to_be32(~0); 8537318166cSFlorian Fainelli flow->m_ext.data[1] ^= cpu_to_be32(~0); 8547318166cSFlorian Fainelli } 8557318166cSFlorian Fainelli 856bc3fc44cSFlorian Fainelli static int bcm_sf2_cfp_unslice_ipv4(struct bcm_sf2_priv *priv, 857bc3fc44cSFlorian Fainelli struct ethtool_tcpip4_spec *v4_spec, 858bc3fc44cSFlorian Fainelli bool mask) 859bc3fc44cSFlorian Fainelli { 860bc3fc44cSFlorian Fainelli u32 reg, offset, ipv4; 861bc3fc44cSFlorian Fainelli u16 src_dst_port; 862bc3fc44cSFlorian Fainelli 863bc3fc44cSFlorian Fainelli if (mask) 864bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(3); 865bc3fc44cSFlorian Fainelli else 866bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(3); 867bc3fc44cSFlorian Fainelli 868bc3fc44cSFlorian Fainelli reg = core_readl(priv, offset); 869bc3fc44cSFlorian Fainelli /* src port [15:8] */ 870bc3fc44cSFlorian Fainelli src_dst_port = reg << 8; 871bc3fc44cSFlorian Fainelli 872bc3fc44cSFlorian Fainelli if (mask) 873bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(2); 874bc3fc44cSFlorian Fainelli else 875bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(2); 876bc3fc44cSFlorian Fainelli 877bc3fc44cSFlorian Fainelli reg = core_readl(priv, offset); 878bc3fc44cSFlorian Fainelli /* src port [7:0] */ 879bc3fc44cSFlorian Fainelli src_dst_port |= (reg >> 24); 880bc3fc44cSFlorian Fainelli 881bc3fc44cSFlorian Fainelli v4_spec->pdst = cpu_to_be16(src_dst_port); 882bc3fc44cSFlorian Fainelli v4_spec->psrc = cpu_to_be16((u16)(reg >> 8)); 883bc3fc44cSFlorian Fainelli 884bc3fc44cSFlorian Fainelli /* IPv4 dst [15:8] */ 885bc3fc44cSFlorian Fainelli ipv4 = (reg & 0xff) << 8; 886bc3fc44cSFlorian Fainelli 887bc3fc44cSFlorian Fainelli if (mask) 888bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(1); 889bc3fc44cSFlorian Fainelli else 890bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(1); 891bc3fc44cSFlorian Fainelli 892bc3fc44cSFlorian Fainelli reg = core_readl(priv, offset); 893bc3fc44cSFlorian Fainelli /* IPv4 dst [31:16] */ 894bc3fc44cSFlorian Fainelli ipv4 |= ((reg >> 8) & 0xffff) << 16; 895bc3fc44cSFlorian Fainelli /* IPv4 dst [7:0] */ 896bc3fc44cSFlorian Fainelli ipv4 |= (reg >> 24) & 0xff; 897bc3fc44cSFlorian Fainelli v4_spec->ip4dst = cpu_to_be32(ipv4); 898bc3fc44cSFlorian Fainelli 899bc3fc44cSFlorian Fainelli /* IPv4 src [15:8] */ 900bc3fc44cSFlorian Fainelli ipv4 = (reg & 0xff) << 8; 901bc3fc44cSFlorian Fainelli 902bc3fc44cSFlorian Fainelli if (mask) 903bc3fc44cSFlorian Fainelli offset = CORE_CFP_MASK_PORT(0); 904bc3fc44cSFlorian Fainelli else 905bc3fc44cSFlorian Fainelli offset = CORE_CFP_DATA_PORT(0); 906bc3fc44cSFlorian Fainelli reg = core_readl(priv, offset); 907bc3fc44cSFlorian Fainelli 908bc3fc44cSFlorian Fainelli /* Once the TCAM is programmed, the mask reflects the slice number 909bc3fc44cSFlorian Fainelli * being matched, don't bother checking it when reading back the 910bc3fc44cSFlorian Fainelli * mask spec 911bc3fc44cSFlorian Fainelli */ 912bc3fc44cSFlorian Fainelli if (!mask && !(reg & SLICE_VALID)) 913bc3fc44cSFlorian Fainelli return -EINVAL; 914bc3fc44cSFlorian Fainelli 915bc3fc44cSFlorian Fainelli /* IPv4 src [7:0] */ 916bc3fc44cSFlorian Fainelli ipv4 |= (reg >> 24) & 0xff; 917bc3fc44cSFlorian Fainelli /* IPv4 src [31:16] */ 918bc3fc44cSFlorian Fainelli ipv4 |= ((reg >> 8) & 0xffff) << 16; 919bc3fc44cSFlorian Fainelli v4_spec->ip4src = cpu_to_be32(ipv4); 920bc3fc44cSFlorian Fainelli 921bc3fc44cSFlorian Fainelli return 0; 922bc3fc44cSFlorian Fainelli } 923bc3fc44cSFlorian Fainelli 92433061458SFlorian Fainelli static int bcm_sf2_cfp_ipv4_rule_get(struct bcm_sf2_priv *priv, int port, 925ba0696c2SFlorian Fainelli struct ethtool_rx_flow_spec *fs) 92633061458SFlorian Fainelli { 927ba0696c2SFlorian Fainelli struct ethtool_tcpip4_spec *v4_spec = NULL, *v4_m_spec = NULL; 928bc3fc44cSFlorian Fainelli u32 reg; 929bc3fc44cSFlorian Fainelli int ret; 93033061458SFlorian Fainelli 931ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); 932ba0696c2SFlorian Fainelli 933ba0696c2SFlorian Fainelli switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) { 934ba0696c2SFlorian Fainelli case IPPROTO_TCP: 935ba0696c2SFlorian Fainelli fs->flow_type = TCP_V4_FLOW; 936ba0696c2SFlorian Fainelli v4_spec = &fs->h_u.tcp_ip4_spec; 937ba0696c2SFlorian Fainelli v4_m_spec = &fs->m_u.tcp_ip4_spec; 938ba0696c2SFlorian Fainelli break; 939ba0696c2SFlorian Fainelli case IPPROTO_UDP: 940ba0696c2SFlorian Fainelli fs->flow_type = UDP_V4_FLOW; 941ba0696c2SFlorian Fainelli v4_spec = &fs->h_u.udp_ip4_spec; 942ba0696c2SFlorian Fainelli v4_m_spec = &fs->m_u.udp_ip4_spec; 943ba0696c2SFlorian Fainelli break; 944ba0696c2SFlorian Fainelli default: 945ba0696c2SFlorian Fainelli return -EINVAL; 946ba0696c2SFlorian Fainelli } 947ba0696c2SFlorian Fainelli 948ba0696c2SFlorian Fainelli fs->m_ext.data[0] = cpu_to_be32((reg >> IP_FRAG_SHIFT) & 1); 949ba0696c2SFlorian Fainelli v4_spec->tos = (reg >> IPTOS_SHIFT) & IPTOS_MASK; 950ba0696c2SFlorian Fainelli 951bc3fc44cSFlorian Fainelli ret = bcm_sf2_cfp_unslice_ipv4(priv, v4_spec, false); 952bc3fc44cSFlorian Fainelli if (ret) 953bc3fc44cSFlorian Fainelli return ret; 95433061458SFlorian Fainelli 955bc3fc44cSFlorian Fainelli return bcm_sf2_cfp_unslice_ipv4(priv, v4_m_spec, true); 95633061458SFlorian Fainelli } 95733061458SFlorian Fainelli 958ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_unslice_ipv6(struct bcm_sf2_priv *priv, 959ba0696c2SFlorian Fainelli __be32 *ip6_addr, __be16 *port, 960dd8eff68SFlorian Fainelli bool mask) 961ba0696c2SFlorian Fainelli { 962dd8eff68SFlorian Fainelli u32 reg, tmp, offset; 963ba0696c2SFlorian Fainelli 964ba0696c2SFlorian Fainelli /* C-Tag [31:24] 965ba0696c2SFlorian Fainelli * UDF_n_B8 [23:8] (port) 966ba0696c2SFlorian Fainelli * UDF_n_B7 (upper) [7:0] (addr[15:8]) 967ba0696c2SFlorian Fainelli */ 968dd8eff68SFlorian Fainelli if (mask) 969dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(4); 970dd8eff68SFlorian Fainelli else 971dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(4); 972dd8eff68SFlorian Fainelli reg = core_readl(priv, offset); 973ba0696c2SFlorian Fainelli *port = cpu_to_be32(reg) >> 8; 974ba0696c2SFlorian Fainelli tmp = (u32)(reg & 0xff) << 8; 975ba0696c2SFlorian Fainelli 976ba0696c2SFlorian Fainelli /* UDF_n_B7 (lower) [31:24] (addr[7:0]) 977ba0696c2SFlorian Fainelli * UDF_n_B6 [23:8] (addr[31:16]) 978ba0696c2SFlorian Fainelli * UDF_n_B5 (upper) [7:0] (addr[47:40]) 979ba0696c2SFlorian Fainelli */ 980dd8eff68SFlorian Fainelli if (mask) 981dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(3); 982dd8eff68SFlorian Fainelli else 983dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(3); 984dd8eff68SFlorian Fainelli reg = core_readl(priv, offset); 985ba0696c2SFlorian Fainelli tmp |= (reg >> 24) & 0xff; 986ba0696c2SFlorian Fainelli tmp |= (u32)((reg >> 8) << 16); 987ba0696c2SFlorian Fainelli ip6_addr[3] = cpu_to_be32(tmp); 988ba0696c2SFlorian Fainelli tmp = (u32)(reg & 0xff) << 8; 989ba0696c2SFlorian Fainelli 990ba0696c2SFlorian Fainelli /* UDF_n_B5 (lower) [31:24] (addr[39:32]) 991ba0696c2SFlorian Fainelli * UDF_n_B4 [23:8] (addr[63:48]) 992ba0696c2SFlorian Fainelli * UDF_n_B3 (upper) [7:0] (addr[79:72]) 993ba0696c2SFlorian Fainelli */ 994dd8eff68SFlorian Fainelli if (mask) 995dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(2); 996dd8eff68SFlorian Fainelli else 997dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(2); 998dd8eff68SFlorian Fainelli reg = core_readl(priv, offset); 999ba0696c2SFlorian Fainelli tmp |= (reg >> 24) & 0xff; 1000ba0696c2SFlorian Fainelli tmp |= (u32)((reg >> 8) << 16); 1001ba0696c2SFlorian Fainelli ip6_addr[2] = cpu_to_be32(tmp); 1002ba0696c2SFlorian Fainelli tmp = (u32)(reg & 0xff) << 8; 1003ba0696c2SFlorian Fainelli 1004ba0696c2SFlorian Fainelli /* UDF_n_B3 (lower) [31:24] (addr[71:64]) 1005ba0696c2SFlorian Fainelli * UDF_n_B2 [23:8] (addr[95:80]) 1006ba0696c2SFlorian Fainelli * UDF_n_B1 (upper) [7:0] (addr[111:104]) 1007ba0696c2SFlorian Fainelli */ 1008dd8eff68SFlorian Fainelli if (mask) 1009dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(1); 1010dd8eff68SFlorian Fainelli else 1011dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(1); 1012dd8eff68SFlorian Fainelli reg = core_readl(priv, offset); 1013ba0696c2SFlorian Fainelli tmp |= (reg >> 24) & 0xff; 1014ba0696c2SFlorian Fainelli tmp |= (u32)((reg >> 8) << 16); 1015ba0696c2SFlorian Fainelli ip6_addr[1] = cpu_to_be32(tmp); 1016ba0696c2SFlorian Fainelli tmp = (u32)(reg & 0xff) << 8; 1017ba0696c2SFlorian Fainelli 1018ba0696c2SFlorian Fainelli /* UDF_n_B1 (lower) [31:24] (addr[103:96]) 1019ba0696c2SFlorian Fainelli * UDF_n_B0 [23:8] (addr[127:112]) 1020ba0696c2SFlorian Fainelli * Reserved [7:4] 1021ba0696c2SFlorian Fainelli * Slice ID [3:2] 1022ba0696c2SFlorian Fainelli * Slice valid [1:0] 1023ba0696c2SFlorian Fainelli */ 1024dd8eff68SFlorian Fainelli if (mask) 1025dd8eff68SFlorian Fainelli offset = CORE_CFP_MASK_PORT(0); 1026dd8eff68SFlorian Fainelli else 1027dd8eff68SFlorian Fainelli offset = CORE_CFP_DATA_PORT(0); 1028dd8eff68SFlorian Fainelli reg = core_readl(priv, offset); 1029ba0696c2SFlorian Fainelli tmp |= (reg >> 24) & 0xff; 1030ba0696c2SFlorian Fainelli tmp |= (u32)((reg >> 8) << 16); 1031ba0696c2SFlorian Fainelli ip6_addr[0] = cpu_to_be32(tmp); 1032ba0696c2SFlorian Fainelli 1033dd8eff68SFlorian Fainelli if (!mask && !(reg & SLICE_VALID)) 1034ba0696c2SFlorian Fainelli return -EINVAL; 1035ba0696c2SFlorian Fainelli 1036ba0696c2SFlorian Fainelli return 0; 1037ba0696c2SFlorian Fainelli } 1038ba0696c2SFlorian Fainelli 1039ba0696c2SFlorian Fainelli static int bcm_sf2_cfp_ipv6_rule_get(struct bcm_sf2_priv *priv, int port, 1040ba0696c2SFlorian Fainelli struct ethtool_rx_flow_spec *fs, 1041ba0696c2SFlorian Fainelli u32 next_loc) 1042ba0696c2SFlorian Fainelli { 1043ba0696c2SFlorian Fainelli struct ethtool_tcpip6_spec *v6_spec = NULL, *v6_m_spec = NULL; 1044ba0696c2SFlorian Fainelli u32 reg; 1045ba0696c2SFlorian Fainelli int ret; 1046ba0696c2SFlorian Fainelli 1047ba0696c2SFlorian Fainelli /* UDPv6 and TCPv6 both use ethtool_tcpip6_spec so we are fine 1048ba0696c2SFlorian Fainelli * assuming tcp_ip6_spec here being an union. 1049ba0696c2SFlorian Fainelli */ 1050ba0696c2SFlorian Fainelli v6_spec = &fs->h_u.tcp_ip6_spec; 1051ba0696c2SFlorian Fainelli v6_m_spec = &fs->m_u.tcp_ip6_spec; 1052ba0696c2SFlorian Fainelli 1053ba0696c2SFlorian Fainelli /* Read the second half first */ 1054ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_spec->ip6dst, &v6_spec->pdst, 1055dd8eff68SFlorian Fainelli false); 1056dd8eff68SFlorian Fainelli if (ret) 1057dd8eff68SFlorian Fainelli return ret; 1058dd8eff68SFlorian Fainelli 1059dd8eff68SFlorian Fainelli ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_m_spec->ip6dst, 1060dd8eff68SFlorian Fainelli &v6_m_spec->pdst, true); 1061ba0696c2SFlorian Fainelli if (ret) 1062ba0696c2SFlorian Fainelli return ret; 1063ba0696c2SFlorian Fainelli 1064ba0696c2SFlorian Fainelli /* Read last to avoid next entry clobbering the results during search 1065ba0696c2SFlorian Fainelli * operations. We would not have the port enabled for this rule, so 1066ba0696c2SFlorian Fainelli * don't bother checking it. 1067ba0696c2SFlorian Fainelli */ 1068ba0696c2SFlorian Fainelli (void)core_readl(priv, CORE_CFP_DATA_PORT(7)); 1069ba0696c2SFlorian Fainelli 1070ba0696c2SFlorian Fainelli /* The slice number is valid, so read the rule we are chained from now 1071ba0696c2SFlorian Fainelli * which is our first half. 1072ba0696c2SFlorian Fainelli */ 1073ba0696c2SFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, next_loc); 1074ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); 1075ba0696c2SFlorian Fainelli if (ret) 1076ba0696c2SFlorian Fainelli return ret; 1077ba0696c2SFlorian Fainelli 1078ba0696c2SFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); 1079ba0696c2SFlorian Fainelli 1080ba0696c2SFlorian Fainelli switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) { 1081ba0696c2SFlorian Fainelli case IPPROTO_TCP: 1082ba0696c2SFlorian Fainelli fs->flow_type = TCP_V6_FLOW; 1083ba0696c2SFlorian Fainelli break; 1084ba0696c2SFlorian Fainelli case IPPROTO_UDP: 1085ba0696c2SFlorian Fainelli fs->flow_type = UDP_V6_FLOW; 1086ba0696c2SFlorian Fainelli break; 1087ba0696c2SFlorian Fainelli default: 1088ba0696c2SFlorian Fainelli return -EINVAL; 1089ba0696c2SFlorian Fainelli } 1090ba0696c2SFlorian Fainelli 1091dd8eff68SFlorian Fainelli ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_spec->ip6src, &v6_spec->psrc, 1092dd8eff68SFlorian Fainelli false); 1093dd8eff68SFlorian Fainelli if (ret) 1094dd8eff68SFlorian Fainelli return ret; 1095dd8eff68SFlorian Fainelli 1096dd8eff68SFlorian Fainelli return bcm_sf2_cfp_unslice_ipv6(priv, v6_m_spec->ip6src, 1097dd8eff68SFlorian Fainelli &v6_m_spec->psrc, true); 1098ba0696c2SFlorian Fainelli } 1099ba0696c2SFlorian Fainelli 11007318166cSFlorian Fainelli static int bcm_sf2_cfp_rule_get(struct bcm_sf2_priv *priv, int port, 11014daa70cfSFlorian Fainelli struct ethtool_rxnfc *nfc) 11027318166cSFlorian Fainelli { 1103ba0696c2SFlorian Fainelli u32 reg, ipv4_or_chain_id; 11047318166cSFlorian Fainelli unsigned int queue_num; 11057318166cSFlorian Fainelli int ret; 11067318166cSFlorian Fainelli 11077318166cSFlorian Fainelli bcm_sf2_cfp_rule_addr_set(priv, nfc->fs.location); 11087318166cSFlorian Fainelli 11097318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | ACT_POL_RAM); 11107318166cSFlorian Fainelli if (ret) 11117318166cSFlorian Fainelli return ret; 11127318166cSFlorian Fainelli 11137318166cSFlorian Fainelli reg = core_readl(priv, CORE_ACT_POL_DATA0); 11147318166cSFlorian Fainelli 11157318166cSFlorian Fainelli ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL); 11167318166cSFlorian Fainelli if (ret) 11177318166cSFlorian Fainelli return ret; 11187318166cSFlorian Fainelli 11197318166cSFlorian Fainelli /* Extract the destination port */ 11207318166cSFlorian Fainelli nfc->fs.ring_cookie = fls((reg >> DST_MAP_IB_SHIFT) & 11217318166cSFlorian Fainelli DST_MAP_IB_MASK) - 1; 11227318166cSFlorian Fainelli 11237318166cSFlorian Fainelli /* There is no Port 6, so we compensate for that here */ 11247318166cSFlorian Fainelli if (nfc->fs.ring_cookie >= 6) 11257318166cSFlorian Fainelli nfc->fs.ring_cookie++; 1126152b6fd6SFlorian Fainelli nfc->fs.ring_cookie *= SF2_NUM_EGRESS_QUEUES; 11277318166cSFlorian Fainelli 11287318166cSFlorian Fainelli /* Extract the destination queue */ 11297318166cSFlorian Fainelli queue_num = (reg >> NEW_TC_SHIFT) & NEW_TC_MASK; 11307318166cSFlorian Fainelli nfc->fs.ring_cookie += queue_num; 11317318166cSFlorian Fainelli 1132ba0696c2SFlorian Fainelli /* Extract the L3_FRAMING or CHAIN_ID */ 11337318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(6)); 11347318166cSFlorian Fainelli 1135ba0696c2SFlorian Fainelli /* With IPv6 rules this would contain a non-zero chain ID since 1136ba0696c2SFlorian Fainelli * we reserve entry 0 and it cannot be used. So if we read 0 here 1137ba0696c2SFlorian Fainelli * this means an IPv4 rule. 1138ba0696c2SFlorian Fainelli */ 1139ba0696c2SFlorian Fainelli ipv4_or_chain_id = (reg >> L3_FRAMING_SHIFT) & 0xff; 1140ba0696c2SFlorian Fainelli if (ipv4_or_chain_id == 0) 1141ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv4_rule_get(priv, port, &nfc->fs); 1142ba0696c2SFlorian Fainelli else 1143ba0696c2SFlorian Fainelli ret = bcm_sf2_cfp_ipv6_rule_get(priv, port, &nfc->fs, 1144ba0696c2SFlorian Fainelli ipv4_or_chain_id); 114533061458SFlorian Fainelli if (ret) 114633061458SFlorian Fainelli return ret; 11477318166cSFlorian Fainelli 11487318166cSFlorian Fainelli /* Read last to avoid next entry clobbering the results during search 11497318166cSFlorian Fainelli * operations 11507318166cSFlorian Fainelli */ 11517318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_DATA_PORT(7)); 11527318166cSFlorian Fainelli if (!(reg & 1 << port)) 11537318166cSFlorian Fainelli return -EINVAL; 11547318166cSFlorian Fainelli 11557318166cSFlorian Fainelli bcm_sf2_invert_masks(&nfc->fs); 11567318166cSFlorian Fainelli 11577318166cSFlorian Fainelli /* Put the TCAM size here */ 11587318166cSFlorian Fainelli nfc->data = bcm_sf2_cfp_rule_size(priv); 11597318166cSFlorian Fainelli 11607318166cSFlorian Fainelli return 0; 11617318166cSFlorian Fainelli } 11627318166cSFlorian Fainelli 11637318166cSFlorian Fainelli /* We implement the search doing a TCAM search operation */ 11647318166cSFlorian Fainelli static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv, 11657318166cSFlorian Fainelli int port, struct ethtool_rxnfc *nfc, 11667318166cSFlorian Fainelli u32 *rule_locs) 11677318166cSFlorian Fainelli { 11687318166cSFlorian Fainelli unsigned int index = 1, rules_cnt = 0; 11697318166cSFlorian Fainelli 1170ba0696c2SFlorian Fainelli for_each_set_bit_from(index, priv->cfp.unique, priv->num_cfp_rules) { 11717318166cSFlorian Fainelli rule_locs[rules_cnt] = index; 11727318166cSFlorian Fainelli rules_cnt++; 11737318166cSFlorian Fainelli } 11747318166cSFlorian Fainelli 11757318166cSFlorian Fainelli /* Put the TCAM size here */ 11767318166cSFlorian Fainelli nfc->data = bcm_sf2_cfp_rule_size(priv); 11777318166cSFlorian Fainelli nfc->rule_cnt = rules_cnt; 11787318166cSFlorian Fainelli 11797318166cSFlorian Fainelli return 0; 11807318166cSFlorian Fainelli } 11817318166cSFlorian Fainelli 11827318166cSFlorian Fainelli int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, 11837318166cSFlorian Fainelli struct ethtool_rxnfc *nfc, u32 *rule_locs) 11847318166cSFlorian Fainelli { 11857318166cSFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 11867318166cSFlorian Fainelli int ret = 0; 11877318166cSFlorian Fainelli 11887318166cSFlorian Fainelli mutex_lock(&priv->cfp.lock); 11897318166cSFlorian Fainelli 11907318166cSFlorian Fainelli switch (nfc->cmd) { 11917318166cSFlorian Fainelli case ETHTOOL_GRXCLSRLCNT: 11927318166cSFlorian Fainelli /* Subtract the default, unusable rule */ 1193ba0696c2SFlorian Fainelli nfc->rule_cnt = bitmap_weight(priv->cfp.unique, 1194df191632SFlorian Fainelli priv->num_cfp_rules) - 1; 11957318166cSFlorian Fainelli /* We support specifying rule locations */ 11967318166cSFlorian Fainelli nfc->data |= RX_CLS_LOC_SPECIAL; 11977318166cSFlorian Fainelli break; 11987318166cSFlorian Fainelli case ETHTOOL_GRXCLSRULE: 11994daa70cfSFlorian Fainelli ret = bcm_sf2_cfp_rule_get(priv, port, nfc); 12007318166cSFlorian Fainelli break; 12017318166cSFlorian Fainelli case ETHTOOL_GRXCLSRLALL: 12027318166cSFlorian Fainelli ret = bcm_sf2_cfp_rule_get_all(priv, port, nfc, rule_locs); 12037318166cSFlorian Fainelli break; 12047318166cSFlorian Fainelli default: 12057318166cSFlorian Fainelli ret = -EOPNOTSUPP; 12067318166cSFlorian Fainelli break; 12077318166cSFlorian Fainelli } 12087318166cSFlorian Fainelli 12097318166cSFlorian Fainelli mutex_unlock(&priv->cfp.lock); 12107318166cSFlorian Fainelli 12117318166cSFlorian Fainelli return ret; 12127318166cSFlorian Fainelli } 12137318166cSFlorian Fainelli 12147318166cSFlorian Fainelli int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, 12157318166cSFlorian Fainelli struct ethtool_rxnfc *nfc) 12167318166cSFlorian Fainelli { 12177318166cSFlorian Fainelli struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 12187318166cSFlorian Fainelli int ret = 0; 12197318166cSFlorian Fainelli 12207318166cSFlorian Fainelli mutex_lock(&priv->cfp.lock); 12217318166cSFlorian Fainelli 12227318166cSFlorian Fainelli switch (nfc->cmd) { 12237318166cSFlorian Fainelli case ETHTOOL_SRXCLSRLINS: 12247318166cSFlorian Fainelli ret = bcm_sf2_cfp_rule_set(ds, port, &nfc->fs); 12257318166cSFlorian Fainelli break; 12267318166cSFlorian Fainelli 12277318166cSFlorian Fainelli case ETHTOOL_SRXCLSRLDEL: 12287318166cSFlorian Fainelli ret = bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location); 12297318166cSFlorian Fainelli break; 12307318166cSFlorian Fainelli default: 12317318166cSFlorian Fainelli ret = -EOPNOTSUPP; 12327318166cSFlorian Fainelli break; 12337318166cSFlorian Fainelli } 12347318166cSFlorian Fainelli 12357318166cSFlorian Fainelli mutex_unlock(&priv->cfp.lock); 12367318166cSFlorian Fainelli 12377318166cSFlorian Fainelli return ret; 12387318166cSFlorian Fainelli } 12397318166cSFlorian Fainelli 12407318166cSFlorian Fainelli int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv) 12417318166cSFlorian Fainelli { 12427318166cSFlorian Fainelli unsigned int timeout = 1000; 12437318166cSFlorian Fainelli u32 reg; 12447318166cSFlorian Fainelli 12457318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 12467318166cSFlorian Fainelli reg |= TCAM_RESET; 12477318166cSFlorian Fainelli core_writel(priv, reg, CORE_CFP_ACC); 12487318166cSFlorian Fainelli 12497318166cSFlorian Fainelli do { 12507318166cSFlorian Fainelli reg = core_readl(priv, CORE_CFP_ACC); 12517318166cSFlorian Fainelli if (!(reg & TCAM_RESET)) 12527318166cSFlorian Fainelli break; 12537318166cSFlorian Fainelli 12547318166cSFlorian Fainelli cpu_relax(); 12557318166cSFlorian Fainelli } while (timeout--); 12567318166cSFlorian Fainelli 12577318166cSFlorian Fainelli if (!timeout) 12587318166cSFlorian Fainelli return -ETIMEDOUT; 12597318166cSFlorian Fainelli 12607318166cSFlorian Fainelli return 0; 12617318166cSFlorian Fainelli } 1262