1347 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Broadcom Starfighter 2 DSA switch CFP support4 *5 * Copyright (C) 2016, Broadcom6 */7 8#include <linux/list.h>9#include <linux/ethtool.h>10#include <linux/if_ether.h>11#include <linux/in.h>12#include <linux/netdevice.h>13#include <net/dsa.h>14#include <linux/bitmap.h>15#include <net/flow_offload.h>16#include <net/switchdev.h>17#include <uapi/linux/if_bridge.h>18 19#include "bcm_sf2.h"20#include "bcm_sf2_regs.h"21 22struct cfp_rule {23 int port;24 struct ethtool_rx_flow_spec fs;25 struct list_head next;26};27 28struct cfp_udf_slice_layout {29 u8 slices[UDFS_PER_SLICE];30 u32 mask_value;31 u32 base_offset;32};33 34struct cfp_udf_layout {35 struct cfp_udf_slice_layout udfs[UDF_NUM_SLICES];36};37 38static const u8 zero_slice[UDFS_PER_SLICE] = { };39 40/* UDF slices layout for a TCPv4/UDPv4 specification */41static const struct cfp_udf_layout udf_tcpip4_layout = {42 .udfs = {43 [1] = {44 .slices = {45 /* End of L2, byte offset 12, src IP[0:15] */46 CFG_UDF_EOL2 | 6,47 /* End of L2, byte offset 14, src IP[16:31] */48 CFG_UDF_EOL2 | 7,49 /* End of L2, byte offset 16, dst IP[0:15] */50 CFG_UDF_EOL2 | 8,51 /* End of L2, byte offset 18, dst IP[16:31] */52 CFG_UDF_EOL2 | 9,53 /* End of L3, byte offset 0, src port */54 CFG_UDF_EOL3 | 0,55 /* End of L3, byte offset 2, dst port */56 CFG_UDF_EOL3 | 1,57 0, 0, 058 },59 .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG,60 .base_offset = CORE_UDF_0_A_0_8_PORT_0 + UDF_SLICE_OFFSET,61 },62 },63};64 65/* UDF slices layout for a TCPv6/UDPv6 specification */66static const struct cfp_udf_layout udf_tcpip6_layout = {67 .udfs = {68 [0] = {69 .slices = {70 /* End of L2, byte offset 8, src IP[0:15] */71 CFG_UDF_EOL2 | 4,72 /* End of L2, byte offset 10, src IP[16:31] */73 CFG_UDF_EOL2 | 5,74 /* End of L2, byte offset 12, src IP[32:47] */75 CFG_UDF_EOL2 | 6,76 /* End of L2, byte offset 14, src IP[48:63] */77 CFG_UDF_EOL2 | 7,78 /* End of L2, byte offset 16, src IP[64:79] */79 CFG_UDF_EOL2 | 8,80 /* End of L2, byte offset 18, src IP[80:95] */81 CFG_UDF_EOL2 | 9,82 /* End of L2, byte offset 20, src IP[96:111] */83 CFG_UDF_EOL2 | 10,84 /* End of L2, byte offset 22, src IP[112:127] */85 CFG_UDF_EOL2 | 11,86 /* End of L3, byte offset 0, src port */87 CFG_UDF_EOL3 | 0,88 },89 .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG,90 .base_offset = CORE_UDF_0_B_0_8_PORT_0,91 },92 [3] = {93 .slices = {94 /* End of L2, byte offset 24, dst IP[0:15] */95 CFG_UDF_EOL2 | 12,96 /* End of L2, byte offset 26, dst IP[16:31] */97 CFG_UDF_EOL2 | 13,98 /* End of L2, byte offset 28, dst IP[32:47] */99 CFG_UDF_EOL2 | 14,100 /* End of L2, byte offset 30, dst IP[48:63] */101 CFG_UDF_EOL2 | 15,102 /* End of L2, byte offset 32, dst IP[64:79] */103 CFG_UDF_EOL2 | 16,104 /* End of L2, byte offset 34, dst IP[80:95] */105 CFG_UDF_EOL2 | 17,106 /* End of L2, byte offset 36, dst IP[96:111] */107 CFG_UDF_EOL2 | 18,108 /* End of L2, byte offset 38, dst IP[112:127] */109 CFG_UDF_EOL2 | 19,110 /* End of L3, byte offset 2, dst port */111 CFG_UDF_EOL3 | 1,112 },113 .mask_value = L3_FRAMING_MASK | IPPROTO_MASK | IP_FRAG,114 .base_offset = CORE_UDF_0_D_0_11_PORT_0,115 },116 },117};118 119static inline unsigned int bcm_sf2_get_num_udf_slices(const u8 *layout)120{121 unsigned int i, count = 0;122 123 for (i = 0; i < UDFS_PER_SLICE; i++) {124 if (layout[i] != 0)125 count++;126 }127 128 return count;129}130 131static inline u32 udf_upper_bits(int num_udf)132{133 return GENMASK(num_udf - 1, 0) >> (UDFS_PER_SLICE - 1);134}135 136static inline u32 udf_lower_bits(int num_udf)137{138 return (u8)GENMASK(num_udf - 1, 0);139}140 141static unsigned int bcm_sf2_get_slice_number(const struct cfp_udf_layout *l,142 unsigned int start)143{144 const struct cfp_udf_slice_layout *slice_layout;145 unsigned int slice_idx;146 147 for (slice_idx = start; slice_idx < UDF_NUM_SLICES; slice_idx++) {148 slice_layout = &l->udfs[slice_idx];149 if (memcmp(slice_layout->slices, zero_slice,150 sizeof(zero_slice)))151 break;152 }153 154 return slice_idx;155}156 157static void bcm_sf2_cfp_udf_set(struct bcm_sf2_priv *priv,158 const struct cfp_udf_layout *layout,159 unsigned int slice_num)160{161 u32 offset = layout->udfs[slice_num].base_offset;162 unsigned int i;163 164 for (i = 0; i < UDFS_PER_SLICE; i++)165 core_writel(priv, layout->udfs[slice_num].slices[i],166 offset + i * 4);167}168 169static int bcm_sf2_cfp_op(struct bcm_sf2_priv *priv, unsigned int op)170{171 unsigned int timeout = 1000;172 u32 reg;173 174 reg = core_readl(priv, CORE_CFP_ACC);175 reg &= ~(OP_SEL_MASK | RAM_SEL_MASK);176 reg |= OP_STR_DONE | op;177 core_writel(priv, reg, CORE_CFP_ACC);178 179 do {180 reg = core_readl(priv, CORE_CFP_ACC);181 if (!(reg & OP_STR_DONE))182 break;183 184 cpu_relax();185 } while (timeout--);186 187 if (!timeout)188 return -ETIMEDOUT;189 190 return 0;191}192 193static inline void bcm_sf2_cfp_rule_addr_set(struct bcm_sf2_priv *priv,194 unsigned int addr)195{196 u32 reg;197 198 WARN_ON(addr >= priv->num_cfp_rules);199 200 reg = core_readl(priv, CORE_CFP_ACC);201 reg &= ~(XCESS_ADDR_MASK << XCESS_ADDR_SHIFT);202 reg |= addr << XCESS_ADDR_SHIFT;203 core_writel(priv, reg, CORE_CFP_ACC);204}205 206static inline unsigned int bcm_sf2_cfp_rule_size(struct bcm_sf2_priv *priv)207{208 /* Entry #0 is reserved */209 return priv->num_cfp_rules - 1;210}211 212static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv,213 unsigned int rule_index,214 int src_port,215 unsigned int port_num,216 unsigned int queue_num,217 bool fwd_map_change)218{219 int ret;220 u32 reg;221 222 /* Replace ARL derived destination with DST_MAP derived, define223 * which port and queue this should be forwarded to.224 */225 if (fwd_map_change)226 reg = CHANGE_FWRD_MAP_IB_REP_ARL |227 BIT(port_num + DST_MAP_IB_SHIFT) |228 CHANGE_TC | queue_num << NEW_TC_SHIFT;229 else230 reg = 0;231 232 /* Enable looping back to the original port */233 if (src_port == port_num)234 reg |= LOOP_BK_EN;235 236 core_writel(priv, reg, CORE_ACT_POL_DATA0);237 238 /* Set classification ID that needs to be put in Broadcom tag */239 core_writel(priv, rule_index << CHAIN_ID_SHIFT, CORE_ACT_POL_DATA1);240 241 core_writel(priv, 0, CORE_ACT_POL_DATA2);242 243 /* Configure policer RAM now */244 ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | ACT_POL_RAM);245 if (ret) {246 pr_err("Policer entry at %d failed\n", rule_index);247 return ret;248 }249 250 /* Disable the policer */251 core_writel(priv, POLICER_MODE_DISABLE, CORE_RATE_METER0);252 253 /* Now the rate meter */254 ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | RATE_METER_RAM);255 if (ret) {256 pr_err("Meter entry at %d failed\n", rule_index);257 return ret;258 }259 260 return 0;261}262 263static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,264 struct flow_dissector_key_ipv4_addrs *addrs,265 struct flow_dissector_key_ports *ports,266 const __be16 vlan_tci,267 unsigned int slice_num, u8 num_udf,268 bool mask)269{270 u32 reg, offset;271 272 /* UDF_Valid[7:0] [31:24]273 * S-Tag [23:8]274 * C-Tag [7:0]275 */276 reg = udf_lower_bits(num_udf) << 24 | be16_to_cpu(vlan_tci) >> 8;277 if (mask)278 core_writel(priv, reg, CORE_CFP_MASK_PORT(5));279 else280 core_writel(priv, reg, CORE_CFP_DATA_PORT(5));281 282 /* C-Tag [31:24]283 * UDF_n_A8 [23:8]284 * UDF_n_A7 [7:0]285 */286 reg = (u32)(be16_to_cpu(vlan_tci) & 0xff) << 24;287 if (mask)288 offset = CORE_CFP_MASK_PORT(4);289 else290 offset = CORE_CFP_DATA_PORT(4);291 core_writel(priv, reg, offset);292 293 /* UDF_n_A7 [31:24]294 * UDF_n_A6 [23:8]295 * UDF_n_A5 [7:0]296 */297 reg = be16_to_cpu(ports->dst) >> 8;298 if (mask)299 offset = CORE_CFP_MASK_PORT(3);300 else301 offset = CORE_CFP_DATA_PORT(3);302 core_writel(priv, reg, offset);303 304 /* UDF_n_A5 [31:24]305 * UDF_n_A4 [23:8]306 * UDF_n_A3 [7:0]307 */308 reg = (be16_to_cpu(ports->dst) & 0xff) << 24 |309 (u32)be16_to_cpu(ports->src) << 8 |310 (be32_to_cpu(addrs->dst) & 0x0000ff00) >> 8;311 if (mask)312 offset = CORE_CFP_MASK_PORT(2);313 else314 offset = CORE_CFP_DATA_PORT(2);315 core_writel(priv, reg, offset);316 317 /* UDF_n_A3 [31:24]318 * UDF_n_A2 [23:8]319 * UDF_n_A1 [7:0]320 */321 reg = (u32)(be32_to_cpu(addrs->dst) & 0xff) << 24 |322 (u32)(be32_to_cpu(addrs->dst) >> 16) << 8 |323 (be32_to_cpu(addrs->src) & 0x0000ff00) >> 8;324 if (mask)325 offset = CORE_CFP_MASK_PORT(1);326 else327 offset = CORE_CFP_DATA_PORT(1);328 core_writel(priv, reg, offset);329 330 /* UDF_n_A1 [31:24]331 * UDF_n_A0 [23:8]332 * Reserved [7:4]333 * Slice ID [3:2]334 * Slice valid [1:0]335 */336 reg = (u32)(be32_to_cpu(addrs->src) & 0xff) << 24 |337 (u32)(be32_to_cpu(addrs->src) >> 16) << 8 |338 SLICE_NUM(slice_num) | SLICE_VALID;339 if (mask)340 offset = CORE_CFP_MASK_PORT(0);341 else342 offset = CORE_CFP_DATA_PORT(0);343 core_writel(priv, reg, offset);344}345 346static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,347 unsigned int port_num,348 unsigned int queue_num,349 struct ethtool_rx_flow_spec *fs)350{351 __be16 vlan_tci = 0, vlan_m_tci = htons(0xffff);352 struct ethtool_rx_flow_spec_input input = {};353 const struct cfp_udf_layout *layout;354 unsigned int slice_num, rule_index;355 struct ethtool_rx_flow_rule *flow;356 struct flow_match_ipv4_addrs ipv4;357 struct flow_match_ports ports;358 struct flow_match_ip ip;359 u8 ip_proto, ip_frag;360 u8 num_udf;361 u32 reg;362 int ret;363 364 switch (fs->flow_type & ~FLOW_EXT) {365 case TCP_V4_FLOW:366 ip_proto = IPPROTO_TCP;367 break;368 case UDP_V4_FLOW:369 ip_proto = IPPROTO_UDP;370 break;371 default:372 return -EINVAL;373 }374 375 ip_frag = !!(be32_to_cpu(fs->h_ext.data[0]) & 1);376 377 /* Extract VLAN TCI */378 if (fs->flow_type & FLOW_EXT) {379 vlan_tci = fs->h_ext.vlan_tci;380 vlan_m_tci = fs->m_ext.vlan_tci;381 }382 383 /* Locate the first rule available */384 if (fs->location == RX_CLS_LOC_ANY)385 rule_index = find_first_zero_bit(priv->cfp.used,386 priv->num_cfp_rules);387 else388 rule_index = fs->location;389 390 if (rule_index > bcm_sf2_cfp_rule_size(priv))391 return -ENOSPC;392 393 input.fs = fs;394 flow = ethtool_rx_flow_rule_create(&input);395 if (IS_ERR(flow))396 return PTR_ERR(flow);397 398 flow_rule_match_ipv4_addrs(flow->rule, &ipv4);399 flow_rule_match_ports(flow->rule, &ports);400 flow_rule_match_ip(flow->rule, &ip);401 402 layout = &udf_tcpip4_layout;403 /* We only use one UDF slice for now */404 slice_num = bcm_sf2_get_slice_number(layout, 0);405 if (slice_num == UDF_NUM_SLICES) {406 ret = -EINVAL;407 goto out_err_flow_rule;408 }409 410 num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices);411 412 /* Apply the UDF layout for this filter */413 bcm_sf2_cfp_udf_set(priv, layout, slice_num);414 415 /* Apply to all packets received through this port */416 core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7));417 418 /* Source port map match */419 core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7));420 421 /* S-Tag status [31:30]422 * C-Tag status [29:28]423 * L2 framing [27:26]424 * L3 framing [25:24]425 * IP ToS [23:16]426 * IP proto [15:08]427 * IP Fragm [7]428 * Non 1st frag [6]429 * IP Authen [5]430 * TTL range [4:3]431 * PPPoE session [2]432 * Reserved [1]433 * UDF_Valid[8] [0]434 */435 core_writel(priv, ip.key->tos << IPTOS_SHIFT |436 ip_proto << IPPROTO_SHIFT | ip_frag << IP_FRAG_SHIFT |437 udf_upper_bits(num_udf),438 CORE_CFP_DATA_PORT(6));439 440 /* Mask with the specific layout for IPv4 packets */441 core_writel(priv, layout->udfs[slice_num].mask_value |442 udf_upper_bits(num_udf), CORE_CFP_MASK_PORT(6));443 444 /* Program the match and the mask */445 bcm_sf2_cfp_slice_ipv4(priv, ipv4.key, ports.key, vlan_tci,446 slice_num, num_udf, false);447 bcm_sf2_cfp_slice_ipv4(priv, ipv4.mask, ports.mask, vlan_m_tci,448 SLICE_NUM_MASK, num_udf, true);449 450 /* Insert into TCAM now */451 bcm_sf2_cfp_rule_addr_set(priv, rule_index);452 453 ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);454 if (ret) {455 pr_err("TCAM entry at addr %d failed\n", rule_index);456 goto out_err_flow_rule;457 }458 459 /* Insert into Action and policer RAMs now */460 ret = bcm_sf2_cfp_act_pol_set(priv, rule_index, port, port_num,461 queue_num, true);462 if (ret)463 goto out_err_flow_rule;464 465 /* Turn on CFP for this rule now */466 reg = core_readl(priv, CORE_CFP_CTL_REG);467 reg |= BIT(port);468 core_writel(priv, reg, CORE_CFP_CTL_REG);469 470 /* Flag the rule as being used and return it */471 set_bit(rule_index, priv->cfp.used);472 set_bit(rule_index, priv->cfp.unique);473 fs->location = rule_index;474 475 return 0;476 477out_err_flow_rule:478 ethtool_rx_flow_rule_destroy(flow);479 return ret;480}481 482static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv,483 const __be32 *ip6_addr, const __be16 port,484 const __be16 vlan_tci,485 unsigned int slice_num, u32 udf_bits,486 bool mask)487{488 u32 reg, tmp, val, offset;489 490 /* UDF_Valid[7:0] [31:24]491 * S-Tag [23:8]492 * C-Tag [7:0]493 */494 reg = udf_bits << 24 | be16_to_cpu(vlan_tci) >> 8;495 if (mask)496 core_writel(priv, reg, CORE_CFP_MASK_PORT(5));497 else498 core_writel(priv, reg, CORE_CFP_DATA_PORT(5));499 500 /* C-Tag [31:24]501 * UDF_n_B8 [23:8] (port)502 * UDF_n_B7 (upper) [7:0] (addr[15:8])503 */504 reg = be32_to_cpu(ip6_addr[3]);505 val = (u32)be16_to_cpu(port) << 8 | ((reg >> 8) & 0xff);506 val |= (u32)(be16_to_cpu(vlan_tci) & 0xff) << 24;507 if (mask)508 offset = CORE_CFP_MASK_PORT(4);509 else510 offset = CORE_CFP_DATA_PORT(4);511 core_writel(priv, val, offset);512 513 /* UDF_n_B7 (lower) [31:24] (addr[7:0])514 * UDF_n_B6 [23:8] (addr[31:16])515 * UDF_n_B5 (upper) [7:0] (addr[47:40])516 */517 tmp = be32_to_cpu(ip6_addr[2]);518 val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 |519 ((tmp >> 8) & 0xff);520 if (mask)521 offset = CORE_CFP_MASK_PORT(3);522 else523 offset = CORE_CFP_DATA_PORT(3);524 core_writel(priv, val, offset);525 526 /* UDF_n_B5 (lower) [31:24] (addr[39:32])527 * UDF_n_B4 [23:8] (addr[63:48])528 * UDF_n_B3 (upper) [7:0] (addr[79:72])529 */530 reg = be32_to_cpu(ip6_addr[1]);531 val = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 |532 ((reg >> 8) & 0xff);533 if (mask)534 offset = CORE_CFP_MASK_PORT(2);535 else536 offset = CORE_CFP_DATA_PORT(2);537 core_writel(priv, val, offset);538 539 /* UDF_n_B3 (lower) [31:24] (addr[71:64])540 * UDF_n_B2 [23:8] (addr[95:80])541 * UDF_n_B1 (upper) [7:0] (addr[111:104])542 */543 tmp = be32_to_cpu(ip6_addr[0]);544 val = (u32)(reg & 0xff) << 24 | (u32)(reg >> 16) << 8 |545 ((tmp >> 8) & 0xff);546 if (mask)547 offset = CORE_CFP_MASK_PORT(1);548 else549 offset = CORE_CFP_DATA_PORT(1);550 core_writel(priv, val, offset);551 552 /* UDF_n_B1 (lower) [31:24] (addr[103:96])553 * UDF_n_B0 [23:8] (addr[127:112])554 * Reserved [7:4]555 * Slice ID [3:2]556 * Slice valid [1:0]557 */558 reg = (u32)(tmp & 0xff) << 24 | (u32)(tmp >> 16) << 8 |559 SLICE_NUM(slice_num) | SLICE_VALID;560 if (mask)561 offset = CORE_CFP_MASK_PORT(0);562 else563 offset = CORE_CFP_DATA_PORT(0);564 core_writel(priv, reg, offset);565}566 567static struct cfp_rule *bcm_sf2_cfp_rule_find(struct bcm_sf2_priv *priv,568 int port, u32 location)569{570 struct cfp_rule *rule;571 572 list_for_each_entry(rule, &priv->cfp.rules_list, next) {573 if (rule->port == port && rule->fs.location == location)574 return rule;575 }576 577 return NULL;578}579 580static int bcm_sf2_cfp_rule_cmp(struct bcm_sf2_priv *priv, int port,581 struct ethtool_rx_flow_spec *fs)582{583 struct cfp_rule *rule = NULL;584 size_t fs_size = 0;585 int ret = 1;586 587 if (list_empty(&priv->cfp.rules_list))588 return ret;589 590 list_for_each_entry(rule, &priv->cfp.rules_list, next) {591 ret = 1;592 if (rule->port != port)593 continue;594 595 if (rule->fs.flow_type != fs->flow_type ||596 rule->fs.ring_cookie != fs->ring_cookie ||597 rule->fs.h_ext.data[0] != fs->h_ext.data[0])598 continue;599 600 switch (fs->flow_type & ~FLOW_EXT) {601 case TCP_V6_FLOW:602 case UDP_V6_FLOW:603 fs_size = sizeof(struct ethtool_tcpip6_spec);604 break;605 case TCP_V4_FLOW:606 case UDP_V4_FLOW:607 fs_size = sizeof(struct ethtool_tcpip4_spec);608 break;609 default:610 continue;611 }612 613 ret = memcmp(&rule->fs.h_u, &fs->h_u, fs_size);614 ret |= memcmp(&rule->fs.m_u, &fs->m_u, fs_size);615 /* Compare VLAN TCI values as well */616 if (rule->fs.flow_type & FLOW_EXT) {617 ret |= rule->fs.h_ext.vlan_tci != fs->h_ext.vlan_tci;618 ret |= rule->fs.m_ext.vlan_tci != fs->m_ext.vlan_tci;619 }620 if (ret == 0)621 break;622 }623 624 return ret;625}626 627static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,628 unsigned int port_num,629 unsigned int queue_num,630 struct ethtool_rx_flow_spec *fs)631{632 __be16 vlan_tci = 0, vlan_m_tci = htons(0xffff);633 struct ethtool_rx_flow_spec_input input = {};634 unsigned int slice_num, rule_index[2];635 const struct cfp_udf_layout *layout;636 struct ethtool_rx_flow_rule *flow;637 struct flow_match_ipv6_addrs ipv6;638 struct flow_match_ports ports;639 u8 ip_proto, ip_frag;640 int ret = 0;641 u8 num_udf;642 u32 reg;643 644 switch (fs->flow_type & ~FLOW_EXT) {645 case TCP_V6_FLOW:646 ip_proto = IPPROTO_TCP;647 break;648 case UDP_V6_FLOW:649 ip_proto = IPPROTO_UDP;650 break;651 default:652 return -EINVAL;653 }654 655 ip_frag = !!(be32_to_cpu(fs->h_ext.data[0]) & 1);656 657 /* Extract VLAN TCI */658 if (fs->flow_type & FLOW_EXT) {659 vlan_tci = fs->h_ext.vlan_tci;660 vlan_m_tci = fs->m_ext.vlan_tci;661 }662 663 layout = &udf_tcpip6_layout;664 slice_num = bcm_sf2_get_slice_number(layout, 0);665 if (slice_num == UDF_NUM_SLICES)666 return -EINVAL;667 668 num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices);669 670 /* Negotiate two indexes, one for the second half which we are chained671 * from, which is what we will return to user-space, and a second one672 * which is used to store its first half. That first half does not673 * allow any choice of placement, so it just needs to find the next674 * available bit. We return the second half as fs->location because675 * that helps with the rule lookup later on since the second half is676 * chained from its first half, we can easily identify IPv6 CFP rules677 * by looking whether they carry a CHAIN_ID.678 *679 * We also want the second half to have a lower rule_index than its680 * first half because the HW search is by incrementing addresses.681 */682 if (fs->location == RX_CLS_LOC_ANY)683 rule_index[1] = find_first_zero_bit(priv->cfp.used,684 priv->num_cfp_rules);685 else686 rule_index[1] = fs->location;687 if (rule_index[1] > bcm_sf2_cfp_rule_size(priv))688 return -ENOSPC;689 690 /* Flag it as used (cleared on error path) such that we can immediately691 * obtain a second one to chain from.692 */693 set_bit(rule_index[1], priv->cfp.used);694 695 rule_index[0] = find_first_zero_bit(priv->cfp.used,696 priv->num_cfp_rules);697 if (rule_index[0] > bcm_sf2_cfp_rule_size(priv)) {698 ret = -ENOSPC;699 goto out_err;700 }701 702 input.fs = fs;703 flow = ethtool_rx_flow_rule_create(&input);704 if (IS_ERR(flow)) {705 ret = PTR_ERR(flow);706 goto out_err;707 }708 flow_rule_match_ipv6_addrs(flow->rule, &ipv6);709 flow_rule_match_ports(flow->rule, &ports);710 711 /* Apply the UDF layout for this filter */712 bcm_sf2_cfp_udf_set(priv, layout, slice_num);713 714 /* Apply to all packets received through this port */715 core_writel(priv, BIT(port), CORE_CFP_DATA_PORT(7));716 717 /* Source port map match */718 core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7));719 720 /* S-Tag status [31:30]721 * C-Tag status [29:28]722 * L2 framing [27:26]723 * L3 framing [25:24]724 * IP ToS [23:16]725 * IP proto [15:08]726 * IP Fragm [7]727 * Non 1st frag [6]728 * IP Authen [5]729 * TTL range [4:3]730 * PPPoE session [2]731 * Reserved [1]732 * UDF_Valid[8] [0]733 */734 reg = 1 << L3_FRAMING_SHIFT | ip_proto << IPPROTO_SHIFT |735 ip_frag << IP_FRAG_SHIFT | udf_upper_bits(num_udf);736 core_writel(priv, reg, CORE_CFP_DATA_PORT(6));737 738 /* Mask with the specific layout for IPv6 packets including739 * UDF_Valid[8]740 */741 reg = layout->udfs[slice_num].mask_value | udf_upper_bits(num_udf);742 core_writel(priv, reg, CORE_CFP_MASK_PORT(6));743 744 /* Slice the IPv6 source address and port */745 bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->src.in6_u.u6_addr32,746 ports.key->src, vlan_tci, slice_num,747 udf_lower_bits(num_udf), false);748 bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->src.in6_u.u6_addr32,749 ports.mask->src, vlan_m_tci, SLICE_NUM_MASK,750 udf_lower_bits(num_udf), true);751 752 /* Insert into TCAM now because we need to insert a second rule */753 bcm_sf2_cfp_rule_addr_set(priv, rule_index[0]);754 755 ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);756 if (ret) {757 pr_err("TCAM entry at addr %d failed\n", rule_index[0]);758 goto out_err_flow_rule;759 }760 761 /* Insert into Action and policer RAMs now */762 ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port, port_num,763 queue_num, false);764 if (ret)765 goto out_err_flow_rule;766 767 /* Now deal with the second slice to chain this rule */768 slice_num = bcm_sf2_get_slice_number(layout, slice_num + 1);769 if (slice_num == UDF_NUM_SLICES) {770 ret = -EINVAL;771 goto out_err_flow_rule;772 }773 774 num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices);775 776 /* Apply the UDF layout for this filter */777 bcm_sf2_cfp_udf_set(priv, layout, slice_num);778 779 /* Chained rule, source port match is coming from the rule we are780 * chained from.781 */782 core_writel(priv, 0, CORE_CFP_DATA_PORT(7));783 core_writel(priv, 0, CORE_CFP_MASK_PORT(7));784 785 /*786 * CHAIN ID [31:24] chain to previous slice787 * Reserved [23:20]788 * UDF_Valid[11:8] [19:16]789 * UDF_Valid[7:0] [15:8]790 * UDF_n_D11 [7:0]791 */792 reg = rule_index[0] << 24 | udf_upper_bits(num_udf) << 16 |793 udf_lower_bits(num_udf) << 8;794 core_writel(priv, reg, CORE_CFP_DATA_PORT(6));795 796 /* Mask all except chain ID, UDF Valid[8] and UDF Valid[7:0] */797 reg = XCESS_ADDR_MASK << 24 | udf_upper_bits(num_udf) << 16 |798 udf_lower_bits(num_udf) << 8;799 core_writel(priv, reg, CORE_CFP_MASK_PORT(6));800 801 bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->dst.in6_u.u6_addr32,802 ports.key->dst, 0, slice_num,803 0, false);804 bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->dst.in6_u.u6_addr32,805 ports.key->dst, 0, SLICE_NUM_MASK,806 0, true);807 808 /* Insert into TCAM now */809 bcm_sf2_cfp_rule_addr_set(priv, rule_index[1]);810 811 ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);812 if (ret) {813 pr_err("TCAM entry at addr %d failed\n", rule_index[1]);814 goto out_err_flow_rule;815 }816 817 /* Insert into Action and policer RAMs now, set chain ID to818 * the one we are chained to819 */820 ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[1], port, port_num,821 queue_num, true);822 if (ret)823 goto out_err_flow_rule;824 825 /* Turn on CFP for this rule now */826 reg = core_readl(priv, CORE_CFP_CTL_REG);827 reg |= BIT(port);828 core_writel(priv, reg, CORE_CFP_CTL_REG);829 830 /* Flag the second half rule as being used now, return it as the831 * location, and flag it as unique while dumping rules832 */833 set_bit(rule_index[0], priv->cfp.used);834 set_bit(rule_index[1], priv->cfp.unique);835 fs->location = rule_index[1];836 837 return ret;838 839out_err_flow_rule:840 ethtool_rx_flow_rule_destroy(flow);841out_err:842 clear_bit(rule_index[1], priv->cfp.used);843 return ret;844}845 846static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port,847 struct ethtool_rx_flow_spec *fs)848{849 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);850 s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index;851 __u64 ring_cookie = fs->ring_cookie;852 struct switchdev_obj_port_vlan vlan;853 unsigned int queue_num, port_num;854 u16 vid;855 int ret;856 857 /* This rule is a Wake-on-LAN filter and we must specifically858 * target the CPU port in order for it to be working.859 */860 if (ring_cookie == RX_CLS_FLOW_WAKE)861 ring_cookie = cpu_port * SF2_NUM_EGRESS_QUEUES;862 863 /* We do not support discarding packets, check that the864 * destination port is enabled and that we are within the865 * number of ports supported by the switch866 */867 port_num = ring_cookie / SF2_NUM_EGRESS_QUEUES;868 869 if (ring_cookie == RX_CLS_FLOW_DISC ||870 !(dsa_is_user_port(ds, port_num) ||871 dsa_is_cpu_port(ds, port_num)) ||872 port_num >= priv->hw_params.num_ports)873 return -EINVAL;874 875 /* If the rule is matching a particular VLAN, make sure that we honor876 * the matching and have it tagged or untagged on the destination port,877 * we do this on egress with a VLAN entry. The egress tagging attribute878 * is expected to be provided in h_ext.data[1] bit 0. A 1 means untagged,879 * a 0 means tagged.880 */881 if (fs->flow_type & FLOW_EXT) {882 /* We cannot support matching multiple VLAN IDs yet */883 if ((be16_to_cpu(fs->m_ext.vlan_tci) & VLAN_VID_MASK) !=884 VLAN_VID_MASK)885 return -EINVAL;886 887 vid = be16_to_cpu(fs->h_ext.vlan_tci) & VLAN_VID_MASK;888 vlan.vid = vid;889 if (be32_to_cpu(fs->h_ext.data[1]) & 1)890 vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED;891 else892 vlan.flags = 0;893 894 ret = ds->ops->port_vlan_add(ds, port_num, &vlan, NULL);895 if (ret)896 return ret;897 }898 899 /*900 * We have a small oddity where Port 6 just does not have a901 * valid bit here (so we substract by one).902 */903 queue_num = ring_cookie % SF2_NUM_EGRESS_QUEUES;904 if (port_num >= 7)905 port_num -= 1;906 907 switch (fs->flow_type & ~FLOW_EXT) {908 case TCP_V4_FLOW:909 case UDP_V4_FLOW:910 ret = bcm_sf2_cfp_ipv4_rule_set(priv, port, port_num,911 queue_num, fs);912 break;913 case TCP_V6_FLOW:914 case UDP_V6_FLOW:915 ret = bcm_sf2_cfp_ipv6_rule_set(priv, port, port_num,916 queue_num, fs);917 break;918 default:919 ret = -EINVAL;920 break;921 }922 923 return ret;924}925 926static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,927 struct ethtool_rx_flow_spec *fs)928{929 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);930 struct cfp_rule *rule = NULL;931 int ret = -EINVAL;932 933 /* Check for unsupported extensions */934 if (fs->flow_type & FLOW_MAC_EXT)935 return -EINVAL;936 937 if (fs->location != RX_CLS_LOC_ANY &&938 fs->location > bcm_sf2_cfp_rule_size(priv))939 return -EINVAL;940 941 if ((fs->flow_type & FLOW_EXT) &&942 !(ds->ops->port_vlan_add || ds->ops->port_vlan_del))943 return -EOPNOTSUPP;944 945 if (fs->location != RX_CLS_LOC_ANY &&946 test_bit(fs->location, priv->cfp.used))947 return -EBUSY;948 949 ret = bcm_sf2_cfp_rule_cmp(priv, port, fs);950 if (ret == 0)951 return -EEXIST;952 953 rule = kzalloc(sizeof(*rule), GFP_KERNEL);954 if (!rule)955 return -ENOMEM;956 957 ret = bcm_sf2_cfp_rule_insert(ds, port, fs);958 if (ret) {959 kfree(rule);960 return ret;961 }962 963 rule->port = port;964 memcpy(&rule->fs, fs, sizeof(*fs));965 list_add_tail(&rule->next, &priv->cfp.rules_list);966 967 return ret;968}969 970static int bcm_sf2_cfp_rule_del_one(struct bcm_sf2_priv *priv, int port,971 u32 loc, u32 *next_loc)972{973 int ret;974 u32 reg;975 976 /* Indicate which rule we want to read */977 bcm_sf2_cfp_rule_addr_set(priv, loc);978 979 ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL);980 if (ret)981 return ret;982 983 /* Check if this is possibly an IPv6 rule that would984 * indicate we need to delete its companion rule985 * as well986 */987 reg = core_readl(priv, CORE_CFP_DATA_PORT(6));988 if (next_loc)989 *next_loc = (reg >> 24) & CHAIN_ID_MASK;990 991 /* Clear its valid bits */992 reg = core_readl(priv, CORE_CFP_DATA_PORT(0));993 reg &= ~SLICE_VALID;994 core_writel(priv, reg, CORE_CFP_DATA_PORT(0));995 996 /* Write back this entry into the TCAM now */997 ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);998 if (ret)999 return ret;1000 1001 clear_bit(loc, priv->cfp.used);1002 clear_bit(loc, priv->cfp.unique);1003 1004 return 0;1005}1006 1007static int bcm_sf2_cfp_rule_remove(struct bcm_sf2_priv *priv, int port,1008 u32 loc)1009{1010 u32 next_loc = 0;1011 int ret;1012 1013 ret = bcm_sf2_cfp_rule_del_one(priv, port, loc, &next_loc);1014 if (ret)1015 return ret;1016 1017 /* If this was an IPv6 rule, delete is companion rule too */1018 if (next_loc)1019 ret = bcm_sf2_cfp_rule_del_one(priv, port, next_loc, NULL);1020 1021 return ret;1022}1023 1024static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, u32 loc)1025{1026 struct cfp_rule *rule;1027 int ret;1028 1029 if (loc > bcm_sf2_cfp_rule_size(priv))1030 return -EINVAL;1031 1032 /* Refuse deleting unused rules, and those that are not unique since1033 * that could leave IPv6 rules with one of the chained rule in the1034 * table.1035 */1036 if (!test_bit(loc, priv->cfp.unique) || loc == 0)1037 return -EINVAL;1038 1039 rule = bcm_sf2_cfp_rule_find(priv, port, loc);1040 if (!rule)1041 return -EINVAL;1042 1043 ret = bcm_sf2_cfp_rule_remove(priv, port, loc);1044 1045 list_del(&rule->next);1046 kfree(rule);1047 1048 return ret;1049}1050 1051static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow)1052{1053 unsigned int i;1054 1055 for (i = 0; i < sizeof(flow->m_u); i++)1056 flow->m_u.hdata[i] ^= 0xff;1057 1058 flow->m_ext.vlan_etype ^= cpu_to_be16(~0);1059 flow->m_ext.vlan_tci ^= cpu_to_be16(~0);1060 flow->m_ext.data[0] ^= cpu_to_be32(~0);1061 flow->m_ext.data[1] ^= cpu_to_be32(~0);1062}1063 1064static int bcm_sf2_cfp_rule_get(struct bcm_sf2_priv *priv, int port,1065 struct ethtool_rxnfc *nfc)1066{1067 struct cfp_rule *rule;1068 1069 rule = bcm_sf2_cfp_rule_find(priv, port, nfc->fs.location);1070 if (!rule)1071 return -EINVAL;1072 1073 memcpy(&nfc->fs, &rule->fs, sizeof(rule->fs));1074 1075 bcm_sf2_invert_masks(&nfc->fs);1076 1077 /* Put the TCAM size here */1078 nfc->data = bcm_sf2_cfp_rule_size(priv);1079 1080 return 0;1081}1082 1083/* We implement the search doing a TCAM search operation */1084static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv,1085 int port, struct ethtool_rxnfc *nfc,1086 u32 *rule_locs)1087{1088 unsigned int index = 1, rules_cnt = 0;1089 1090 for_each_set_bit_from(index, priv->cfp.unique, priv->num_cfp_rules) {1091 rule_locs[rules_cnt] = index;1092 rules_cnt++;1093 }1094 1095 /* Put the TCAM size here */1096 nfc->data = bcm_sf2_cfp_rule_size(priv);1097 nfc->rule_cnt = rules_cnt;1098 1099 return 0;1100}1101 1102int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,1103 struct ethtool_rxnfc *nfc, u32 *rule_locs)1104{1105 struct net_device *p = dsa_port_to_conduit(dsa_to_port(ds, port));1106 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);1107 int ret = 0;1108 1109 mutex_lock(&priv->cfp.lock);1110 1111 switch (nfc->cmd) {1112 case ETHTOOL_GRXCLSRLCNT:1113 /* Subtract the default, unusable rule */1114 nfc->rule_cnt = bitmap_weight(priv->cfp.unique,1115 priv->num_cfp_rules) - 1;1116 /* We support specifying rule locations */1117 nfc->data |= RX_CLS_LOC_SPECIAL;1118 break;1119 case ETHTOOL_GRXCLSRULE:1120 ret = bcm_sf2_cfp_rule_get(priv, port, nfc);1121 break;1122 case ETHTOOL_GRXCLSRLALL:1123 ret = bcm_sf2_cfp_rule_get_all(priv, port, nfc, rule_locs);1124 break;1125 default:1126 ret = -EOPNOTSUPP;1127 break;1128 }1129 1130 mutex_unlock(&priv->cfp.lock);1131 1132 if (ret)1133 return ret;1134 1135 /* Pass up the commands to the attached master network device */1136 if (p->ethtool_ops->get_rxnfc) {1137 ret = p->ethtool_ops->get_rxnfc(p, nfc, rule_locs);1138 if (ret == -EOPNOTSUPP)1139 ret = 0;1140 }1141 1142 return ret;1143}1144 1145int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,1146 struct ethtool_rxnfc *nfc)1147{1148 struct net_device *p = dsa_port_to_conduit(dsa_to_port(ds, port));1149 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);1150 int ret = 0;1151 1152 mutex_lock(&priv->cfp.lock);1153 1154 switch (nfc->cmd) {1155 case ETHTOOL_SRXCLSRLINS:1156 ret = bcm_sf2_cfp_rule_set(ds, port, &nfc->fs);1157 break;1158 1159 case ETHTOOL_SRXCLSRLDEL:1160 ret = bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location);1161 break;1162 default:1163 ret = -EOPNOTSUPP;1164 break;1165 }1166 1167 mutex_unlock(&priv->cfp.lock);1168 1169 if (ret)1170 return ret;1171 1172 /* Pass up the commands to the attached master network device.1173 * This can fail, so rollback the operation if we need to.1174 */1175 if (p->ethtool_ops->set_rxnfc) {1176 ret = p->ethtool_ops->set_rxnfc(p, nfc);1177 if (ret && ret != -EOPNOTSUPP) {1178 mutex_lock(&priv->cfp.lock);1179 bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location);1180 mutex_unlock(&priv->cfp.lock);1181 } else {1182 ret = 0;1183 }1184 }1185 1186 return ret;1187}1188 1189int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv)1190{1191 unsigned int timeout = 1000;1192 u32 reg;1193 1194 reg = core_readl(priv, CORE_CFP_ACC);1195 reg |= TCAM_RESET;1196 core_writel(priv, reg, CORE_CFP_ACC);1197 1198 do {1199 reg = core_readl(priv, CORE_CFP_ACC);1200 if (!(reg & TCAM_RESET))1201 break;1202 1203 cpu_relax();1204 } while (timeout--);1205 1206 if (!timeout)1207 return -ETIMEDOUT;1208 1209 return 0;1210}1211 1212void bcm_sf2_cfp_exit(struct dsa_switch *ds)1213{1214 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);1215 struct cfp_rule *rule, *n;1216 1217 if (list_empty(&priv->cfp.rules_list))1218 return;1219 1220 list_for_each_entry_safe_reverse(rule, n, &priv->cfp.rules_list, next)1221 bcm_sf2_cfp_rule_del(priv, rule->port, rule->fs.location);1222}1223 1224int bcm_sf2_cfp_resume(struct dsa_switch *ds)1225{1226 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);1227 struct cfp_rule *rule;1228 int ret = 0;1229 u32 reg;1230 1231 if (list_empty(&priv->cfp.rules_list))1232 return ret;1233 1234 reg = core_readl(priv, CORE_CFP_CTL_REG);1235 reg &= ~CFP_EN_MAP_MASK;1236 core_writel(priv, reg, CORE_CFP_CTL_REG);1237 1238 ret = bcm_sf2_cfp_rst(priv);1239 if (ret)1240 return ret;1241 1242 list_for_each_entry(rule, &priv->cfp.rules_list, next) {1243 ret = bcm_sf2_cfp_rule_remove(priv, rule->port,1244 rule->fs.location);1245 if (ret) {1246 dev_err(ds->dev, "failed to remove rule\n");1247 return ret;1248 }1249 1250 ret = bcm_sf2_cfp_rule_insert(ds, rule->port, &rule->fs);1251 if (ret) {1252 dev_err(ds->dev, "failed to restore rule\n");1253 return ret;1254 }1255 }1256 1257 return ret;1258}1259 1260static const struct bcm_sf2_cfp_stat {1261 unsigned int offset;1262 unsigned int ram_loc;1263 const char *name;1264} bcm_sf2_cfp_stats[] = {1265 {1266 .offset = CORE_STAT_GREEN_CNTR,1267 .ram_loc = GREEN_STAT_RAM,1268 .name = "Green"1269 },1270 {1271 .offset = CORE_STAT_YELLOW_CNTR,1272 .ram_loc = YELLOW_STAT_RAM,1273 .name = "Yellow"1274 },1275 {1276 .offset = CORE_STAT_RED_CNTR,1277 .ram_loc = RED_STAT_RAM,1278 .name = "Red"1279 },1280};1281 1282void bcm_sf2_cfp_get_strings(struct dsa_switch *ds, int port,1283 u32 stringset, uint8_t *data)1284{1285 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);1286 unsigned int s = ARRAY_SIZE(bcm_sf2_cfp_stats);1287 char buf[ETH_GSTRING_LEN];1288 unsigned int i, j, iter;1289 1290 if (stringset != ETH_SS_STATS)1291 return;1292 1293 for (i = 1; i < priv->num_cfp_rules; i++) {1294 for (j = 0; j < s; j++) {1295 snprintf(buf, sizeof(buf),1296 "CFP%03d_%sCntr",1297 i, bcm_sf2_cfp_stats[j].name);1298 iter = (i - 1) * s + j;1299 strscpy(data + iter * ETH_GSTRING_LEN,1300 buf, ETH_GSTRING_LEN);1301 }1302 }1303}1304 1305void bcm_sf2_cfp_get_ethtool_stats(struct dsa_switch *ds, int port,1306 uint64_t *data)1307{1308 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);1309 unsigned int s = ARRAY_SIZE(bcm_sf2_cfp_stats);1310 const struct bcm_sf2_cfp_stat *stat;1311 unsigned int i, j, iter;1312 struct cfp_rule *rule;1313 int ret;1314 1315 mutex_lock(&priv->cfp.lock);1316 for (i = 1; i < priv->num_cfp_rules; i++) {1317 rule = bcm_sf2_cfp_rule_find(priv, port, i);1318 if (!rule)1319 continue;1320 1321 for (j = 0; j < s; j++) {1322 stat = &bcm_sf2_cfp_stats[j];1323 1324 bcm_sf2_cfp_rule_addr_set(priv, i);1325 ret = bcm_sf2_cfp_op(priv, stat->ram_loc | OP_SEL_READ);1326 if (ret)1327 continue;1328 1329 iter = (i - 1) * s + j;1330 data[iter] = core_readl(priv, stat->offset);1331 }1332 1333 }1334 mutex_unlock(&priv->cfp.lock);1335}1336 1337int bcm_sf2_cfp_get_sset_count(struct dsa_switch *ds, int port, int sset)1338{1339 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);1340 1341 if (sset != ETH_SS_STATS)1342 return 0;1343 1344 /* 3 counters per CFP rules */1345 return (priv->num_cfp_rules - 1) * ARRAY_SIZE(bcm_sf2_cfp_stats);1346}1347