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