100a76783SLorenzo Bianconi // SPDX-License-Identifier: GPL-2.0-only 200a76783SLorenzo Bianconi /* 300a76783SLorenzo Bianconi * Copyright (c) 2025 AIROHA Inc 400a76783SLorenzo Bianconi * Author: Lorenzo Bianconi <lorenzo@kernel.org> 500a76783SLorenzo Bianconi */ 600a76783SLorenzo Bianconi 700a76783SLorenzo Bianconi #include <linux/ip.h> 800a76783SLorenzo Bianconi #include <linux/ipv6.h> 900a76783SLorenzo Bianconi #include <linux/rhashtable.h> 1000a76783SLorenzo Bianconi #include <net/ipv6.h> 1100a76783SLorenzo Bianconi #include <net/pkt_cls.h> 1200a76783SLorenzo Bianconi 1300a76783SLorenzo Bianconi #include "airoha_npu.h" 1400a76783SLorenzo Bianconi #include "airoha_regs.h" 1500a76783SLorenzo Bianconi #include "airoha_eth.h" 1600a76783SLorenzo Bianconi 1700a76783SLorenzo Bianconi static DEFINE_MUTEX(flow_offload_mutex); 1800a76783SLorenzo Bianconi static DEFINE_SPINLOCK(ppe_lock); 1900a76783SLorenzo Bianconi 2000a76783SLorenzo Bianconi static const struct rhashtable_params airoha_flow_table_params = { 2100a76783SLorenzo Bianconi .head_offset = offsetof(struct airoha_flow_table_entry, node), 2200a76783SLorenzo Bianconi .key_offset = offsetof(struct airoha_flow_table_entry, cookie), 2300a76783SLorenzo Bianconi .key_len = sizeof(unsigned long), 2400a76783SLorenzo Bianconi .automatic_shrinking = true, 2500a76783SLorenzo Bianconi }; 2600a76783SLorenzo Bianconi 27b4916f67SLorenzo Bianconi static const struct rhashtable_params airoha_l2_flow_table_params = { 28b4916f67SLorenzo Bianconi .head_offset = offsetof(struct airoha_flow_table_entry, l2_node), 29b4916f67SLorenzo Bianconi .key_offset = offsetof(struct airoha_flow_table_entry, data.bridge), 30b4916f67SLorenzo Bianconi .key_len = 2 * ETH_ALEN, 31b4916f67SLorenzo Bianconi .automatic_shrinking = true, 32b4916f67SLorenzo Bianconi }; 33b4916f67SLorenzo Bianconi 3400a76783SLorenzo Bianconi static bool airoha_ppe2_is_enabled(struct airoha_eth *eth) 3500a76783SLorenzo Bianconi { 3600a76783SLorenzo Bianconi return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK; 3700a76783SLorenzo Bianconi } 3800a76783SLorenzo Bianconi 3900a76783SLorenzo Bianconi static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) 4000a76783SLorenzo Bianconi { 4100a76783SLorenzo Bianconi u16 timestamp = airoha_fe_rr(ppe->eth, REG_FE_FOE_TS); 4200a76783SLorenzo Bianconi 4300a76783SLorenzo Bianconi return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp); 4400a76783SLorenzo Bianconi } 4500a76783SLorenzo Bianconi 4600a76783SLorenzo Bianconi static void airoha_ppe_hw_init(struct airoha_ppe *ppe) 4700a76783SLorenzo Bianconi { 4800a76783SLorenzo Bianconi u32 sram_tb_size, sram_num_entries, dram_num_entries; 4900a76783SLorenzo Bianconi struct airoha_eth *eth = ppe->eth; 5000a76783SLorenzo Bianconi int i; 5100a76783SLorenzo Bianconi 5200a76783SLorenzo Bianconi sram_tb_size = PPE_SRAM_NUM_ENTRIES * sizeof(struct airoha_foe_entry); 5300a76783SLorenzo Bianconi dram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(PPE_DRAM_NUM_ENTRIES); 5400a76783SLorenzo Bianconi 5500a76783SLorenzo Bianconi for (i = 0; i < PPE_NUM; i++) { 5600a76783SLorenzo Bianconi int p; 5700a76783SLorenzo Bianconi 5800a76783SLorenzo Bianconi airoha_fe_wr(eth, REG_PPE_TB_BASE(i), 5900a76783SLorenzo Bianconi ppe->foe_dma + sram_tb_size); 6000a76783SLorenzo Bianconi 6100a76783SLorenzo Bianconi airoha_fe_rmw(eth, REG_PPE_BND_AGE0(i), 6200a76783SLorenzo Bianconi PPE_BIND_AGE0_DELTA_NON_L4 | 6300a76783SLorenzo Bianconi PPE_BIND_AGE0_DELTA_UDP, 6400a76783SLorenzo Bianconi FIELD_PREP(PPE_BIND_AGE0_DELTA_NON_L4, 1) | 6500a76783SLorenzo Bianconi FIELD_PREP(PPE_BIND_AGE0_DELTA_UDP, 12)); 6600a76783SLorenzo Bianconi airoha_fe_rmw(eth, REG_PPE_BND_AGE1(i), 6700a76783SLorenzo Bianconi PPE_BIND_AGE1_DELTA_TCP_FIN | 6800a76783SLorenzo Bianconi PPE_BIND_AGE1_DELTA_TCP, 6900a76783SLorenzo Bianconi FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP_FIN, 1) | 7000a76783SLorenzo Bianconi FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP, 7)); 7100a76783SLorenzo Bianconi 7200a76783SLorenzo Bianconi airoha_fe_rmw(eth, REG_PPE_TB_HASH_CFG(i), 7300a76783SLorenzo Bianconi PPE_SRAM_TABLE_EN_MASK | 7400a76783SLorenzo Bianconi PPE_SRAM_HASH1_EN_MASK | 7500a76783SLorenzo Bianconi PPE_DRAM_TABLE_EN_MASK | 7600a76783SLorenzo Bianconi PPE_SRAM_HASH0_MODE_MASK | 7700a76783SLorenzo Bianconi PPE_SRAM_HASH1_MODE_MASK | 7800a76783SLorenzo Bianconi PPE_DRAM_HASH0_MODE_MASK | 7900a76783SLorenzo Bianconi PPE_DRAM_HASH1_MODE_MASK, 8000a76783SLorenzo Bianconi FIELD_PREP(PPE_SRAM_TABLE_EN_MASK, 1) | 8100a76783SLorenzo Bianconi FIELD_PREP(PPE_SRAM_HASH1_EN_MASK, 1) | 8200a76783SLorenzo Bianconi FIELD_PREP(PPE_SRAM_HASH1_MODE_MASK, 1) | 8300a76783SLorenzo Bianconi FIELD_PREP(PPE_DRAM_HASH1_MODE_MASK, 3)); 8400a76783SLorenzo Bianconi 8500a76783SLorenzo Bianconi airoha_fe_rmw(eth, REG_PPE_TB_CFG(i), 8600a76783SLorenzo Bianconi PPE_TB_CFG_SEARCH_MISS_MASK | 87a98326c1SLorenzo Bianconi PPE_TB_CFG_KEEPALIVE_MASK | 8800a76783SLorenzo Bianconi PPE_TB_ENTRY_SIZE_MASK, 8900a76783SLorenzo Bianconi FIELD_PREP(PPE_TB_CFG_SEARCH_MISS_MASK, 3) | 9000a76783SLorenzo Bianconi FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0)); 9100a76783SLorenzo Bianconi 9200a76783SLorenzo Bianconi airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED); 9300a76783SLorenzo Bianconi 9400a76783SLorenzo Bianconi for (p = 0; p < ARRAY_SIZE(eth->ports); p++) 9500a76783SLorenzo Bianconi airoha_fe_rmw(eth, REG_PPE_MTU(i, p), 9600a76783SLorenzo Bianconi FP0_EGRESS_MTU_MASK | 9700a76783SLorenzo Bianconi FP1_EGRESS_MTU_MASK, 9800a76783SLorenzo Bianconi FIELD_PREP(FP0_EGRESS_MTU_MASK, 9900a76783SLorenzo Bianconi AIROHA_MAX_MTU) | 10000a76783SLorenzo Bianconi FIELD_PREP(FP1_EGRESS_MTU_MASK, 10100a76783SLorenzo Bianconi AIROHA_MAX_MTU)); 10200a76783SLorenzo Bianconi } 10300a76783SLorenzo Bianconi 10400a76783SLorenzo Bianconi if (airoha_ppe2_is_enabled(eth)) { 10500a76783SLorenzo Bianconi sram_num_entries = 106b81e0f2bSLorenzo Bianconi PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_DATA_ENTRIES); 10700a76783SLorenzo Bianconi airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), 10800a76783SLorenzo Bianconi PPE_SRAM_TB_NUM_ENTRY_MASK | 10900a76783SLorenzo Bianconi PPE_DRAM_TB_NUM_ENTRY_MASK, 11000a76783SLorenzo Bianconi FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, 11100a76783SLorenzo Bianconi sram_num_entries) | 11200a76783SLorenzo Bianconi FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, 11300a76783SLorenzo Bianconi dram_num_entries)); 11400a76783SLorenzo Bianconi airoha_fe_rmw(eth, REG_PPE_TB_CFG(1), 11500a76783SLorenzo Bianconi PPE_SRAM_TB_NUM_ENTRY_MASK | 11600a76783SLorenzo Bianconi PPE_DRAM_TB_NUM_ENTRY_MASK, 11700a76783SLorenzo Bianconi FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, 11800a76783SLorenzo Bianconi sram_num_entries) | 11900a76783SLorenzo Bianconi FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, 12000a76783SLorenzo Bianconi dram_num_entries)); 12100a76783SLorenzo Bianconi } else { 12200a76783SLorenzo Bianconi sram_num_entries = 123b81e0f2bSLorenzo Bianconi PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_DATA_ENTRIES); 12400a76783SLorenzo Bianconi airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), 12500a76783SLorenzo Bianconi PPE_SRAM_TB_NUM_ENTRY_MASK | 12600a76783SLorenzo Bianconi PPE_DRAM_TB_NUM_ENTRY_MASK, 12700a76783SLorenzo Bianconi FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, 12800a76783SLorenzo Bianconi sram_num_entries) | 12900a76783SLorenzo Bianconi FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, 13000a76783SLorenzo Bianconi dram_num_entries)); 13100a76783SLorenzo Bianconi } 13200a76783SLorenzo Bianconi } 13300a76783SLorenzo Bianconi 13400a76783SLorenzo Bianconi static void airoha_ppe_flow_mangle_eth(const struct flow_action_entry *act, void *eth) 13500a76783SLorenzo Bianconi { 13600a76783SLorenzo Bianconi void *dest = eth + act->mangle.offset; 13700a76783SLorenzo Bianconi const void *src = &act->mangle.val; 13800a76783SLorenzo Bianconi 13900a76783SLorenzo Bianconi if (act->mangle.offset > 8) 14000a76783SLorenzo Bianconi return; 14100a76783SLorenzo Bianconi 14200a76783SLorenzo Bianconi if (act->mangle.mask == 0xffff) { 14300a76783SLorenzo Bianconi src += 2; 14400a76783SLorenzo Bianconi dest += 2; 14500a76783SLorenzo Bianconi } 14600a76783SLorenzo Bianconi 14700a76783SLorenzo Bianconi memcpy(dest, src, act->mangle.mask ? 2 : 4); 14800a76783SLorenzo Bianconi } 14900a76783SLorenzo Bianconi 15000a76783SLorenzo Bianconi static int airoha_ppe_flow_mangle_ports(const struct flow_action_entry *act, 15100a76783SLorenzo Bianconi struct airoha_flow_data *data) 15200a76783SLorenzo Bianconi { 15300a76783SLorenzo Bianconi u32 val = be32_to_cpu((__force __be32)act->mangle.val); 15400a76783SLorenzo Bianconi 15500a76783SLorenzo Bianconi switch (act->mangle.offset) { 15600a76783SLorenzo Bianconi case 0: 15700a76783SLorenzo Bianconi if ((__force __be32)act->mangle.mask == ~cpu_to_be32(0xffff)) 15800a76783SLorenzo Bianconi data->dst_port = cpu_to_be16(val); 15900a76783SLorenzo Bianconi else 16000a76783SLorenzo Bianconi data->src_port = cpu_to_be16(val >> 16); 16100a76783SLorenzo Bianconi break; 16200a76783SLorenzo Bianconi case 2: 16300a76783SLorenzo Bianconi data->dst_port = cpu_to_be16(val); 16400a76783SLorenzo Bianconi break; 16500a76783SLorenzo Bianconi default: 16600a76783SLorenzo Bianconi return -EINVAL; 16700a76783SLorenzo Bianconi } 16800a76783SLorenzo Bianconi 16900a76783SLorenzo Bianconi return 0; 17000a76783SLorenzo Bianconi } 17100a76783SLorenzo Bianconi 17200a76783SLorenzo Bianconi static int airoha_ppe_flow_mangle_ipv4(const struct flow_action_entry *act, 17300a76783SLorenzo Bianconi struct airoha_flow_data *data) 17400a76783SLorenzo Bianconi { 17500a76783SLorenzo Bianconi __be32 *dest; 17600a76783SLorenzo Bianconi 17700a76783SLorenzo Bianconi switch (act->mangle.offset) { 17800a76783SLorenzo Bianconi case offsetof(struct iphdr, saddr): 17900a76783SLorenzo Bianconi dest = &data->v4.src_addr; 18000a76783SLorenzo Bianconi break; 18100a76783SLorenzo Bianconi case offsetof(struct iphdr, daddr): 18200a76783SLorenzo Bianconi dest = &data->v4.dst_addr; 18300a76783SLorenzo Bianconi break; 18400a76783SLorenzo Bianconi default: 18500a76783SLorenzo Bianconi return -EINVAL; 18600a76783SLorenzo Bianconi } 18700a76783SLorenzo Bianconi 18800a76783SLorenzo Bianconi memcpy(dest, &act->mangle.val, sizeof(u32)); 18900a76783SLorenzo Bianconi 19000a76783SLorenzo Bianconi return 0; 19100a76783SLorenzo Bianconi } 19200a76783SLorenzo Bianconi 19300a76783SLorenzo Bianconi static int airoha_get_dsa_port(struct net_device **dev) 19400a76783SLorenzo Bianconi { 19500a76783SLorenzo Bianconi #if IS_ENABLED(CONFIG_NET_DSA) 19600a76783SLorenzo Bianconi struct dsa_port *dp = dsa_port_from_netdev(*dev); 19700a76783SLorenzo Bianconi 19800a76783SLorenzo Bianconi if (IS_ERR(dp)) 19900a76783SLorenzo Bianconi return -ENODEV; 20000a76783SLorenzo Bianconi 20100a76783SLorenzo Bianconi *dev = dsa_port_to_conduit(dp); 20200a76783SLorenzo Bianconi return dp->index; 20300a76783SLorenzo Bianconi #else 20400a76783SLorenzo Bianconi return -ENODEV; 20500a76783SLorenzo Bianconi #endif 20600a76783SLorenzo Bianconi } 20700a76783SLorenzo Bianconi 208cd53f622SLorenzo Bianconi static void airoha_ppe_foe_set_bridge_addrs(struct airoha_foe_bridge *br, 209cd53f622SLorenzo Bianconi struct ethhdr *eh) 210cd53f622SLorenzo Bianconi { 211cd53f622SLorenzo Bianconi br->dest_mac_hi = get_unaligned_be32(eh->h_dest); 212cd53f622SLorenzo Bianconi br->dest_mac_lo = get_unaligned_be16(eh->h_dest + 4); 213cd53f622SLorenzo Bianconi br->src_mac_hi = get_unaligned_be16(eh->h_source); 214cd53f622SLorenzo Bianconi br->src_mac_lo = get_unaligned_be32(eh->h_source + 2); 215cd53f622SLorenzo Bianconi } 216cd53f622SLorenzo Bianconi 21709bccf56SLorenzo Bianconi static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, 21809bccf56SLorenzo Bianconi struct airoha_foe_entry *hwe, 21900a76783SLorenzo Bianconi struct net_device *dev, int type, 22000a76783SLorenzo Bianconi struct airoha_flow_data *data, 22100a76783SLorenzo Bianconi int l4proto) 22200a76783SLorenzo Bianconi { 22300a76783SLorenzo Bianconi int dsa_port = airoha_get_dsa_port(&dev); 22400a76783SLorenzo Bianconi struct airoha_foe_mac_info_common *l2; 22500a76783SLorenzo Bianconi u32 qdata, ports_pad, val; 226a869d3a5SLorenzo Bianconi u8 smac_id = 0xf; 22700a76783SLorenzo Bianconi 22800a76783SLorenzo Bianconi memset(hwe, 0, sizeof(*hwe)); 22900a76783SLorenzo Bianconi 23000a76783SLorenzo Bianconi val = FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, AIROHA_FOE_STATE_BIND) | 23100a76783SLorenzo Bianconi FIELD_PREP(AIROHA_FOE_IB1_BIND_PACKET_TYPE, type) | 23200a76783SLorenzo Bianconi FIELD_PREP(AIROHA_FOE_IB1_BIND_UDP, l4proto == IPPROTO_UDP) | 23300a76783SLorenzo Bianconi FIELD_PREP(AIROHA_FOE_IB1_BIND_VLAN_LAYER, data->vlan.num) | 23400a76783SLorenzo Bianconi FIELD_PREP(AIROHA_FOE_IB1_BIND_VPM, data->vlan.num) | 23500a76783SLorenzo Bianconi AIROHA_FOE_IB1_BIND_TTL; 23600a76783SLorenzo Bianconi hwe->ib1 = val; 23700a76783SLorenzo Bianconi 2389cd451d4SLorenzo Bianconi val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f) | 2399cd451d4SLorenzo Bianconi AIROHA_FOE_IB2_PSE_QOS; 24000a76783SLorenzo Bianconi if (dsa_port >= 0) 24100a76783SLorenzo Bianconi val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, dsa_port); 24200a76783SLorenzo Bianconi 24300a76783SLorenzo Bianconi if (dev) { 24400a76783SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 24500a76783SLorenzo Bianconi u8 pse_port; 24600a76783SLorenzo Bianconi 24709bccf56SLorenzo Bianconi if (!airoha_is_valid_gdm_port(eth, port)) 24809bccf56SLorenzo Bianconi return -EINVAL; 24909bccf56SLorenzo Bianconi 2509cd451d4SLorenzo Bianconi if (dsa_port >= 0) 25100a76783SLorenzo Bianconi pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; 2529cd451d4SLorenzo Bianconi else 2539cd451d4SLorenzo Bianconi pse_port = 2; /* uplink relies on GDM2 loopback */ 25400a76783SLorenzo Bianconi val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port); 255c683e378SLorenzo Bianconi 256c683e378SLorenzo Bianconi /* For downlink traffic consume SRAM memory for hw forwarding 257c683e378SLorenzo Bianconi * descriptors queue. 258c683e378SLorenzo Bianconi */ 259c683e378SLorenzo Bianconi if (airhoa_is_lan_gdm_port(port)) 260c683e378SLorenzo Bianconi val |= AIROHA_FOE_IB2_FAST_PATH; 261a869d3a5SLorenzo Bianconi 262a869d3a5SLorenzo Bianconi smac_id = port->id; 26300a76783SLorenzo Bianconi } 26400a76783SLorenzo Bianconi 26500a76783SLorenzo Bianconi if (is_multicast_ether_addr(data->eth.h_dest)) 26600a76783SLorenzo Bianconi val |= AIROHA_FOE_IB2_MULTICAST; 26700a76783SLorenzo Bianconi 26800a76783SLorenzo Bianconi ports_pad = 0xa5a5a500 | (l4proto & 0xff); 26900a76783SLorenzo Bianconi if (type == PPE_PKT_TYPE_IPV4_ROUTE) 27000a76783SLorenzo Bianconi hwe->ipv4.orig_tuple.ports = ports_pad; 27100a76783SLorenzo Bianconi if (type == PPE_PKT_TYPE_IPV6_ROUTE_3T) 27200a76783SLorenzo Bianconi hwe->ipv6.ports = ports_pad; 27300a76783SLorenzo Bianconi 27400a76783SLorenzo Bianconi qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f); 27500a76783SLorenzo Bianconi if (type == PPE_PKT_TYPE_BRIDGE) { 276cd53f622SLorenzo Bianconi airoha_ppe_foe_set_bridge_addrs(&hwe->bridge, &data->eth); 27700a76783SLorenzo Bianconi hwe->bridge.data = qdata; 27800a76783SLorenzo Bianconi hwe->bridge.ib2 = val; 27900a76783SLorenzo Bianconi l2 = &hwe->bridge.l2.common; 28000a76783SLorenzo Bianconi } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { 28100a76783SLorenzo Bianconi hwe->ipv6.data = qdata; 28200a76783SLorenzo Bianconi hwe->ipv6.ib2 = val; 28300a76783SLorenzo Bianconi l2 = &hwe->ipv6.l2; 28400a76783SLorenzo Bianconi } else { 28500a76783SLorenzo Bianconi hwe->ipv4.data = qdata; 28600a76783SLorenzo Bianconi hwe->ipv4.ib2 = val; 28700a76783SLorenzo Bianconi l2 = &hwe->ipv4.l2.common; 28800a76783SLorenzo Bianconi } 28900a76783SLorenzo Bianconi 29000a76783SLorenzo Bianconi l2->dest_mac_hi = get_unaligned_be32(data->eth.h_dest); 29100a76783SLorenzo Bianconi l2->dest_mac_lo = get_unaligned_be16(data->eth.h_dest + 4); 29200a76783SLorenzo Bianconi if (type <= PPE_PKT_TYPE_IPV4_DSLITE) { 29300a76783SLorenzo Bianconi l2->src_mac_hi = get_unaligned_be32(data->eth.h_source); 29400a76783SLorenzo Bianconi hwe->ipv4.l2.src_mac_lo = 29500a76783SLorenzo Bianconi get_unaligned_be16(data->eth.h_source + 4); 29600a76783SLorenzo Bianconi } else { 297a869d3a5SLorenzo Bianconi l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, smac_id); 29800a76783SLorenzo Bianconi } 29900a76783SLorenzo Bianconi 30000a76783SLorenzo Bianconi if (data->vlan.num) { 30100a76783SLorenzo Bianconi l2->etype = dsa_port >= 0 ? BIT(dsa_port) : 0; 30200a76783SLorenzo Bianconi l2->vlan1 = data->vlan.hdr[0].id; 30300a76783SLorenzo Bianconi if (data->vlan.num == 2) 30400a76783SLorenzo Bianconi l2->vlan2 = data->vlan.hdr[1].id; 30500a76783SLorenzo Bianconi } else if (dsa_port >= 0) { 30600a76783SLorenzo Bianconi l2->etype = BIT(15) | BIT(dsa_port); 30700a76783SLorenzo Bianconi } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { 30800a76783SLorenzo Bianconi l2->etype = ETH_P_IPV6; 30900a76783SLorenzo Bianconi } else { 31000a76783SLorenzo Bianconi l2->etype = ETH_P_IP; 31100a76783SLorenzo Bianconi } 31200a76783SLorenzo Bianconi 31300a76783SLorenzo Bianconi return 0; 31400a76783SLorenzo Bianconi } 31500a76783SLorenzo Bianconi 31600a76783SLorenzo Bianconi static int airoha_ppe_foe_entry_set_ipv4_tuple(struct airoha_foe_entry *hwe, 31700a76783SLorenzo Bianconi struct airoha_flow_data *data, 31800a76783SLorenzo Bianconi bool egress) 31900a76783SLorenzo Bianconi { 32000a76783SLorenzo Bianconi int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); 32100a76783SLorenzo Bianconi struct airoha_foe_ipv4_tuple *t; 32200a76783SLorenzo Bianconi 32300a76783SLorenzo Bianconi switch (type) { 32400a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_HNAPT: 32500a76783SLorenzo Bianconi if (egress) { 32600a76783SLorenzo Bianconi t = &hwe->ipv4.new_tuple; 32700a76783SLorenzo Bianconi break; 32800a76783SLorenzo Bianconi } 32900a76783SLorenzo Bianconi fallthrough; 33000a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_DSLITE: 33100a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_ROUTE: 33200a76783SLorenzo Bianconi t = &hwe->ipv4.orig_tuple; 33300a76783SLorenzo Bianconi break; 33400a76783SLorenzo Bianconi default: 33500a76783SLorenzo Bianconi WARN_ON_ONCE(1); 33600a76783SLorenzo Bianconi return -EINVAL; 33700a76783SLorenzo Bianconi } 33800a76783SLorenzo Bianconi 33900a76783SLorenzo Bianconi t->src_ip = be32_to_cpu(data->v4.src_addr); 34000a76783SLorenzo Bianconi t->dest_ip = be32_to_cpu(data->v4.dst_addr); 34100a76783SLorenzo Bianconi 34200a76783SLorenzo Bianconi if (type != PPE_PKT_TYPE_IPV4_ROUTE) { 34300a76783SLorenzo Bianconi t->src_port = be16_to_cpu(data->src_port); 34400a76783SLorenzo Bianconi t->dest_port = be16_to_cpu(data->dst_port); 34500a76783SLorenzo Bianconi } 34600a76783SLorenzo Bianconi 34700a76783SLorenzo Bianconi return 0; 34800a76783SLorenzo Bianconi } 34900a76783SLorenzo Bianconi 35000a76783SLorenzo Bianconi static int airoha_ppe_foe_entry_set_ipv6_tuple(struct airoha_foe_entry *hwe, 35100a76783SLorenzo Bianconi struct airoha_flow_data *data) 35200a76783SLorenzo Bianconi 35300a76783SLorenzo Bianconi { 35400a76783SLorenzo Bianconi int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); 35500a76783SLorenzo Bianconi u32 *src, *dest; 35600a76783SLorenzo Bianconi 35700a76783SLorenzo Bianconi switch (type) { 35800a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV6_ROUTE_5T: 35900a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV6_6RD: 36000a76783SLorenzo Bianconi hwe->ipv6.src_port = be16_to_cpu(data->src_port); 36100a76783SLorenzo Bianconi hwe->ipv6.dest_port = be16_to_cpu(data->dst_port); 36200a76783SLorenzo Bianconi fallthrough; 36300a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV6_ROUTE_3T: 36400a76783SLorenzo Bianconi src = hwe->ipv6.src_ip; 36500a76783SLorenzo Bianconi dest = hwe->ipv6.dest_ip; 36600a76783SLorenzo Bianconi break; 36700a76783SLorenzo Bianconi default: 36800a76783SLorenzo Bianconi WARN_ON_ONCE(1); 36900a76783SLorenzo Bianconi return -EINVAL; 37000a76783SLorenzo Bianconi } 37100a76783SLorenzo Bianconi 37200a76783SLorenzo Bianconi ipv6_addr_be32_to_cpu(src, data->v6.src_addr.s6_addr32); 37300a76783SLorenzo Bianconi ipv6_addr_be32_to_cpu(dest, data->v6.dst_addr.s6_addr32); 37400a76783SLorenzo Bianconi 37500a76783SLorenzo Bianconi return 0; 37600a76783SLorenzo Bianconi } 37700a76783SLorenzo Bianconi 37800a76783SLorenzo Bianconi static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe) 37900a76783SLorenzo Bianconi { 38000a76783SLorenzo Bianconi int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); 38100a76783SLorenzo Bianconi u32 hash, hv1, hv2, hv3; 38200a76783SLorenzo Bianconi 38300a76783SLorenzo Bianconi switch (type) { 38400a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_ROUTE: 38500a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_HNAPT: 38600a76783SLorenzo Bianconi hv1 = hwe->ipv4.orig_tuple.ports; 38700a76783SLorenzo Bianconi hv2 = hwe->ipv4.orig_tuple.dest_ip; 38800a76783SLorenzo Bianconi hv3 = hwe->ipv4.orig_tuple.src_ip; 38900a76783SLorenzo Bianconi break; 39000a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV6_ROUTE_3T: 39100a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV6_ROUTE_5T: 39200a76783SLorenzo Bianconi hv1 = hwe->ipv6.src_ip[3] ^ hwe->ipv6.dest_ip[3]; 39300a76783SLorenzo Bianconi hv1 ^= hwe->ipv6.ports; 39400a76783SLorenzo Bianconi 39500a76783SLorenzo Bianconi hv2 = hwe->ipv6.src_ip[2] ^ hwe->ipv6.dest_ip[2]; 39600a76783SLorenzo Bianconi hv2 ^= hwe->ipv6.dest_ip[0]; 39700a76783SLorenzo Bianconi 39800a76783SLorenzo Bianconi hv3 = hwe->ipv6.src_ip[1] ^ hwe->ipv6.dest_ip[1]; 39900a76783SLorenzo Bianconi hv3 ^= hwe->ipv6.src_ip[0]; 40000a76783SLorenzo Bianconi break; 401cd53f622SLorenzo Bianconi case PPE_PKT_TYPE_BRIDGE: { 402cd53f622SLorenzo Bianconi struct airoha_foe_mac_info *l2 = &hwe->bridge.l2; 403cd53f622SLorenzo Bianconi 404cd53f622SLorenzo Bianconi hv1 = l2->common.src_mac_hi & 0xffff; 405cd53f622SLorenzo Bianconi hv1 = hv1 << 16 | l2->src_mac_lo; 406cd53f622SLorenzo Bianconi 407cd53f622SLorenzo Bianconi hv2 = l2->common.dest_mac_lo; 408cd53f622SLorenzo Bianconi hv2 = hv2 << 16; 409cd53f622SLorenzo Bianconi hv2 = hv2 | ((l2->common.src_mac_hi & 0xffff0000) >> 16); 410cd53f622SLorenzo Bianconi 411cd53f622SLorenzo Bianconi hv3 = l2->common.dest_mac_hi; 412cd53f622SLorenzo Bianconi break; 413cd53f622SLorenzo Bianconi } 41400a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_DSLITE: 41500a76783SLorenzo Bianconi case PPE_PKT_TYPE_IPV6_6RD: 41600a76783SLorenzo Bianconi default: 41700a76783SLorenzo Bianconi WARN_ON_ONCE(1); 41800a76783SLorenzo Bianconi return PPE_HASH_MASK; 41900a76783SLorenzo Bianconi } 42000a76783SLorenzo Bianconi 42100a76783SLorenzo Bianconi hash = (hv1 & hv2) | ((~hv1) & hv3); 42200a76783SLorenzo Bianconi hash = (hash >> 24) | ((hash & 0xffffff) << 8); 42300a76783SLorenzo Bianconi hash ^= hv1 ^ hv2 ^ hv3; 42400a76783SLorenzo Bianconi hash ^= hash >> 16; 42500a76783SLorenzo Bianconi hash &= PPE_NUM_ENTRIES - 1; 42600a76783SLorenzo Bianconi 42700a76783SLorenzo Bianconi return hash; 42800a76783SLorenzo Bianconi } 42900a76783SLorenzo Bianconi 430b81e0f2bSLorenzo Bianconi static u32 airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe, u32 hash) 431b81e0f2bSLorenzo Bianconi { 432b81e0f2bSLorenzo Bianconi if (!airoha_ppe2_is_enabled(ppe->eth)) 433b81e0f2bSLorenzo Bianconi return hash; 434b81e0f2bSLorenzo Bianconi 435b81e0f2bSLorenzo Bianconi return hash >= PPE_STATS_NUM_ENTRIES ? hash - PPE1_STATS_NUM_ENTRIES 436b81e0f2bSLorenzo Bianconi : hash; 437b81e0f2bSLorenzo Bianconi } 438b81e0f2bSLorenzo Bianconi 439b81e0f2bSLorenzo Bianconi static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe, 440b81e0f2bSLorenzo Bianconi struct airoha_npu *npu, 441b81e0f2bSLorenzo Bianconi int index) 442b81e0f2bSLorenzo Bianconi { 443b81e0f2bSLorenzo Bianconi memset_io(&npu->stats[index], 0, sizeof(*npu->stats)); 444b81e0f2bSLorenzo Bianconi memset(&ppe->foe_stats[index], 0, sizeof(*ppe->foe_stats)); 445b81e0f2bSLorenzo Bianconi } 446b81e0f2bSLorenzo Bianconi 447b81e0f2bSLorenzo Bianconi static void airoha_ppe_foe_flow_stats_reset(struct airoha_ppe *ppe, 448b81e0f2bSLorenzo Bianconi struct airoha_npu *npu) 449b81e0f2bSLorenzo Bianconi { 450b81e0f2bSLorenzo Bianconi int i; 451b81e0f2bSLorenzo Bianconi 452b81e0f2bSLorenzo Bianconi for (i = 0; i < PPE_STATS_NUM_ENTRIES; i++) 453b81e0f2bSLorenzo Bianconi airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, i); 454b81e0f2bSLorenzo Bianconi } 455b81e0f2bSLorenzo Bianconi 456b81e0f2bSLorenzo Bianconi static void airoha_ppe_foe_flow_stats_update(struct airoha_ppe *ppe, 457b81e0f2bSLorenzo Bianconi struct airoha_npu *npu, 458b81e0f2bSLorenzo Bianconi struct airoha_foe_entry *hwe, 459b81e0f2bSLorenzo Bianconi u32 hash) 460b81e0f2bSLorenzo Bianconi { 461b81e0f2bSLorenzo Bianconi int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); 462b81e0f2bSLorenzo Bianconi u32 index, pse_port, val, *data, *ib2, *meter; 463b81e0f2bSLorenzo Bianconi u8 nbq; 464b81e0f2bSLorenzo Bianconi 465b81e0f2bSLorenzo Bianconi index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); 466b81e0f2bSLorenzo Bianconi if (index >= PPE_STATS_NUM_ENTRIES) 467b81e0f2bSLorenzo Bianconi return; 468b81e0f2bSLorenzo Bianconi 469b81e0f2bSLorenzo Bianconi if (type == PPE_PKT_TYPE_BRIDGE) { 470b81e0f2bSLorenzo Bianconi data = &hwe->bridge.data; 471b81e0f2bSLorenzo Bianconi ib2 = &hwe->bridge.ib2; 472b81e0f2bSLorenzo Bianconi meter = &hwe->bridge.l2.meter; 473b81e0f2bSLorenzo Bianconi } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { 474b81e0f2bSLorenzo Bianconi data = &hwe->ipv6.data; 475b81e0f2bSLorenzo Bianconi ib2 = &hwe->ipv6.ib2; 476b81e0f2bSLorenzo Bianconi meter = &hwe->ipv6.meter; 477b81e0f2bSLorenzo Bianconi } else { 478b81e0f2bSLorenzo Bianconi data = &hwe->ipv4.data; 479b81e0f2bSLorenzo Bianconi ib2 = &hwe->ipv4.ib2; 480b81e0f2bSLorenzo Bianconi meter = &hwe->ipv4.l2.meter; 481b81e0f2bSLorenzo Bianconi } 482b81e0f2bSLorenzo Bianconi 483b81e0f2bSLorenzo Bianconi airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, index); 484b81e0f2bSLorenzo Bianconi 485b81e0f2bSLorenzo Bianconi val = FIELD_GET(AIROHA_FOE_CHANNEL | AIROHA_FOE_QID, *data); 486b81e0f2bSLorenzo Bianconi *data = (*data & ~AIROHA_FOE_ACTDP) | 487b81e0f2bSLorenzo Bianconi FIELD_PREP(AIROHA_FOE_ACTDP, val); 488b81e0f2bSLorenzo Bianconi 489b81e0f2bSLorenzo Bianconi val = *ib2 & (AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | 490b81e0f2bSLorenzo Bianconi AIROHA_FOE_IB2_PSE_QOS | AIROHA_FOE_IB2_FAST_PATH); 491b81e0f2bSLorenzo Bianconi *meter |= FIELD_PREP(AIROHA_FOE_TUNNEL_MTU, val); 492b81e0f2bSLorenzo Bianconi 493b81e0f2bSLorenzo Bianconi pse_port = FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, *ib2); 494b81e0f2bSLorenzo Bianconi nbq = pse_port == 1 ? 6 : 5; 495b81e0f2bSLorenzo Bianconi *ib2 &= ~(AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | 496b81e0f2bSLorenzo Bianconi AIROHA_FOE_IB2_PSE_QOS); 497b81e0f2bSLorenzo Bianconi *ib2 |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, 6) | 498b81e0f2bSLorenzo Bianconi FIELD_PREP(AIROHA_FOE_IB2_NBQ, nbq); 499b81e0f2bSLorenzo Bianconi } 500b81e0f2bSLorenzo Bianconi 5013fe15c64SLorenzo Bianconi struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, 5023fe15c64SLorenzo Bianconi u32 hash) 50300a76783SLorenzo Bianconi { 50400a76783SLorenzo Bianconi if (hash < PPE_SRAM_NUM_ENTRIES) { 50500a76783SLorenzo Bianconi u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry); 50600a76783SLorenzo Bianconi struct airoha_eth *eth = ppe->eth; 50700a76783SLorenzo Bianconi bool ppe2; 50800a76783SLorenzo Bianconi u32 val; 50900a76783SLorenzo Bianconi int i; 51000a76783SLorenzo Bianconi 51100a76783SLorenzo Bianconi ppe2 = airoha_ppe2_is_enabled(ppe->eth) && 51200a76783SLorenzo Bianconi hash >= PPE1_SRAM_NUM_ENTRIES; 51300a76783SLorenzo Bianconi airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2), 51400a76783SLorenzo Bianconi FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) | 51500a76783SLorenzo Bianconi PPE_SRAM_CTRL_REQ_MASK); 51600a76783SLorenzo Bianconi if (read_poll_timeout_atomic(airoha_fe_rr, val, 51700a76783SLorenzo Bianconi val & PPE_SRAM_CTRL_ACK_MASK, 51800a76783SLorenzo Bianconi 10, 100, false, eth, 51900a76783SLorenzo Bianconi REG_PPE_RAM_CTRL(ppe2))) 52000a76783SLorenzo Bianconi return NULL; 52100a76783SLorenzo Bianconi 52200a76783SLorenzo Bianconi for (i = 0; i < sizeof(struct airoha_foe_entry) / 4; i++) 52300a76783SLorenzo Bianconi hwe[i] = airoha_fe_rr(eth, 52400a76783SLorenzo Bianconi REG_PPE_RAM_ENTRY(ppe2, i)); 52500a76783SLorenzo Bianconi } 52600a76783SLorenzo Bianconi 52700a76783SLorenzo Bianconi return ppe->foe + hash * sizeof(struct airoha_foe_entry); 52800a76783SLorenzo Bianconi } 52900a76783SLorenzo Bianconi 53000a76783SLorenzo Bianconi static bool airoha_ppe_foe_compare_entry(struct airoha_flow_table_entry *e, 53100a76783SLorenzo Bianconi struct airoha_foe_entry *hwe) 53200a76783SLorenzo Bianconi { 53300a76783SLorenzo Bianconi int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, e->data.ib1); 53400a76783SLorenzo Bianconi int len; 53500a76783SLorenzo Bianconi 53600a76783SLorenzo Bianconi if ((hwe->ib1 ^ e->data.ib1) & AIROHA_FOE_IB1_BIND_UDP) 53700a76783SLorenzo Bianconi return false; 53800a76783SLorenzo Bianconi 53900a76783SLorenzo Bianconi if (type > PPE_PKT_TYPE_IPV4_DSLITE) 54000a76783SLorenzo Bianconi len = offsetof(struct airoha_foe_entry, ipv6.data); 54100a76783SLorenzo Bianconi else 54200a76783SLorenzo Bianconi len = offsetof(struct airoha_foe_entry, ipv4.ib2); 54300a76783SLorenzo Bianconi 54400a76783SLorenzo Bianconi return !memcmp(&e->data.d, &hwe->d, len - sizeof(hwe->ib1)); 54500a76783SLorenzo Bianconi } 54600a76783SLorenzo Bianconi 54700a76783SLorenzo Bianconi static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe, 54800a76783SLorenzo Bianconi struct airoha_foe_entry *e, 54900a76783SLorenzo Bianconi u32 hash) 55000a76783SLorenzo Bianconi { 55100a76783SLorenzo Bianconi struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe); 55200a76783SLorenzo Bianconi u32 ts = airoha_ppe_get_timestamp(ppe); 55300a76783SLorenzo Bianconi struct airoha_eth *eth = ppe->eth; 554b81e0f2bSLorenzo Bianconi struct airoha_npu *npu; 555b81e0f2bSLorenzo Bianconi int err = 0; 55600a76783SLorenzo Bianconi 55700a76783SLorenzo Bianconi memcpy(&hwe->d, &e->d, sizeof(*hwe) - sizeof(hwe->ib1)); 55800a76783SLorenzo Bianconi wmb(); 55900a76783SLorenzo Bianconi 56000a76783SLorenzo Bianconi e->ib1 &= ~AIROHA_FOE_IB1_BIND_TIMESTAMP; 56100a76783SLorenzo Bianconi e->ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_TIMESTAMP, ts); 56200a76783SLorenzo Bianconi hwe->ib1 = e->ib1; 56300a76783SLorenzo Bianconi 564b81e0f2bSLorenzo Bianconi rcu_read_lock(); 565b81e0f2bSLorenzo Bianconi 566b81e0f2bSLorenzo Bianconi npu = rcu_dereference(eth->npu); 567b81e0f2bSLorenzo Bianconi if (!npu) { 568b81e0f2bSLorenzo Bianconi err = -ENODEV; 569b81e0f2bSLorenzo Bianconi goto unlock; 570b81e0f2bSLorenzo Bianconi } 571b81e0f2bSLorenzo Bianconi 572b81e0f2bSLorenzo Bianconi airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); 573b81e0f2bSLorenzo Bianconi 57400a76783SLorenzo Bianconi if (hash < PPE_SRAM_NUM_ENTRIES) { 57500a76783SLorenzo Bianconi dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); 57600a76783SLorenzo Bianconi bool ppe2 = airoha_ppe2_is_enabled(eth) && 57700a76783SLorenzo Bianconi hash >= PPE1_SRAM_NUM_ENTRIES; 57800a76783SLorenzo Bianconi 579b81e0f2bSLorenzo Bianconi err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe), 580b81e0f2bSLorenzo Bianconi hash, ppe2); 581b81e0f2bSLorenzo Bianconi } 582b81e0f2bSLorenzo Bianconi unlock: 58300a76783SLorenzo Bianconi rcu_read_unlock(); 58400a76783SLorenzo Bianconi 58500a76783SLorenzo Bianconi return err; 58600a76783SLorenzo Bianconi } 58700a76783SLorenzo Bianconi 588b4916f67SLorenzo Bianconi static void airoha_ppe_foe_remove_flow(struct airoha_ppe *ppe, 589b4916f67SLorenzo Bianconi struct airoha_flow_table_entry *e) 590b4916f67SLorenzo Bianconi { 591b4916f67SLorenzo Bianconi lockdep_assert_held(&ppe_lock); 592b4916f67SLorenzo Bianconi 593b4916f67SLorenzo Bianconi hlist_del_init(&e->list); 594b4916f67SLorenzo Bianconi if (e->hash != 0xffff) { 595b4916f67SLorenzo Bianconi e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; 596b4916f67SLorenzo Bianconi e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, 597b4916f67SLorenzo Bianconi AIROHA_FOE_STATE_INVALID); 598b4916f67SLorenzo Bianconi airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); 599b4916f67SLorenzo Bianconi e->hash = 0xffff; 600b4916f67SLorenzo Bianconi } 601cd53f622SLorenzo Bianconi if (e->type == FLOW_TYPE_L2_SUBFLOW) { 602cd53f622SLorenzo Bianconi hlist_del_init(&e->l2_subflow_node); 603cd53f622SLorenzo Bianconi kfree(e); 604cd53f622SLorenzo Bianconi } 605b4916f67SLorenzo Bianconi } 606b4916f67SLorenzo Bianconi 607b4916f67SLorenzo Bianconi static void airoha_ppe_foe_remove_l2_flow(struct airoha_ppe *ppe, 608b4916f67SLorenzo Bianconi struct airoha_flow_table_entry *e) 609b4916f67SLorenzo Bianconi { 610cd53f622SLorenzo Bianconi struct hlist_head *head = &e->l2_flows; 611cd53f622SLorenzo Bianconi struct hlist_node *n; 612cd53f622SLorenzo Bianconi 613b4916f67SLorenzo Bianconi lockdep_assert_held(&ppe_lock); 614b4916f67SLorenzo Bianconi 615b4916f67SLorenzo Bianconi rhashtable_remove_fast(&ppe->l2_flows, &e->l2_node, 616b4916f67SLorenzo Bianconi airoha_l2_flow_table_params); 617cd53f622SLorenzo Bianconi hlist_for_each_entry_safe(e, n, head, l2_subflow_node) 618cd53f622SLorenzo Bianconi airoha_ppe_foe_remove_flow(ppe, e); 619b4916f67SLorenzo Bianconi } 620b4916f67SLorenzo Bianconi 621b4916f67SLorenzo Bianconi static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, 622b4916f67SLorenzo Bianconi struct airoha_flow_table_entry *e) 623b4916f67SLorenzo Bianconi { 624b4916f67SLorenzo Bianconi spin_lock_bh(&ppe_lock); 625b4916f67SLorenzo Bianconi 626b4916f67SLorenzo Bianconi if (e->type == FLOW_TYPE_L2) 627b4916f67SLorenzo Bianconi airoha_ppe_foe_remove_l2_flow(ppe, e); 628b4916f67SLorenzo Bianconi else 629b4916f67SLorenzo Bianconi airoha_ppe_foe_remove_flow(ppe, e); 630b4916f67SLorenzo Bianconi 631b4916f67SLorenzo Bianconi spin_unlock_bh(&ppe_lock); 632b4916f67SLorenzo Bianconi } 633b4916f67SLorenzo Bianconi 634cd53f622SLorenzo Bianconi static int 635cd53f622SLorenzo Bianconi airoha_ppe_foe_commit_subflow_entry(struct airoha_ppe *ppe, 636cd53f622SLorenzo Bianconi struct airoha_flow_table_entry *e, 637cd53f622SLorenzo Bianconi u32 hash) 638cd53f622SLorenzo Bianconi { 639cd53f622SLorenzo Bianconi u32 mask = AIROHA_FOE_IB1_BIND_PACKET_TYPE | AIROHA_FOE_IB1_BIND_UDP; 640cd53f622SLorenzo Bianconi struct airoha_foe_entry *hwe_p, hwe; 641cd53f622SLorenzo Bianconi struct airoha_flow_table_entry *f; 642cd53f622SLorenzo Bianconi int type; 643cd53f622SLorenzo Bianconi 644cd53f622SLorenzo Bianconi hwe_p = airoha_ppe_foe_get_entry(ppe, hash); 645cd53f622SLorenzo Bianconi if (!hwe_p) 646cd53f622SLorenzo Bianconi return -EINVAL; 647cd53f622SLorenzo Bianconi 648cd53f622SLorenzo Bianconi f = kzalloc(sizeof(*f), GFP_ATOMIC); 649cd53f622SLorenzo Bianconi if (!f) 650cd53f622SLorenzo Bianconi return -ENOMEM; 651cd53f622SLorenzo Bianconi 652cd53f622SLorenzo Bianconi hlist_add_head(&f->l2_subflow_node, &e->l2_flows); 653cd53f622SLorenzo Bianconi f->type = FLOW_TYPE_L2_SUBFLOW; 654cd53f622SLorenzo Bianconi f->hash = hash; 655cd53f622SLorenzo Bianconi 656cd53f622SLorenzo Bianconi memcpy(&hwe, hwe_p, sizeof(*hwe_p)); 657cd53f622SLorenzo Bianconi hwe.ib1 = (hwe.ib1 & mask) | (e->data.ib1 & ~mask); 658cd53f622SLorenzo Bianconi 659cd53f622SLorenzo Bianconi type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe.ib1); 660504a577cSLorenzo Bianconi if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { 661504a577cSLorenzo Bianconi memcpy(&hwe.ipv6.l2, &e->data.bridge.l2, sizeof(hwe.ipv6.l2)); 662504a577cSLorenzo Bianconi hwe.ipv6.ib2 = e->data.bridge.ib2; 663c86fac53SLorenzo Bianconi /* setting smac_id to 0xf instruct the hw to keep original 664c86fac53SLorenzo Bianconi * source mac address 665c86fac53SLorenzo Bianconi */ 666c86fac53SLorenzo Bianconi hwe.ipv6.l2.src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, 667c86fac53SLorenzo Bianconi 0xf); 668504a577cSLorenzo Bianconi } else { 669504a577cSLorenzo Bianconi memcpy(&hwe.bridge.l2, &e->data.bridge.l2, 670504a577cSLorenzo Bianconi sizeof(hwe.bridge.l2)); 671504a577cSLorenzo Bianconi hwe.bridge.ib2 = e->data.bridge.ib2; 672cd53f622SLorenzo Bianconi if (type == PPE_PKT_TYPE_IPV4_HNAPT) 673cd53f622SLorenzo Bianconi memcpy(&hwe.ipv4.new_tuple, &hwe.ipv4.orig_tuple, 674cd53f622SLorenzo Bianconi sizeof(hwe.ipv4.new_tuple)); 675504a577cSLorenzo Bianconi } 676cd53f622SLorenzo Bianconi 677b81e0f2bSLorenzo Bianconi hwe.bridge.data = e->data.bridge.data; 678cd53f622SLorenzo Bianconi airoha_ppe_foe_commit_entry(ppe, &hwe, hash); 679cd53f622SLorenzo Bianconi 680cd53f622SLorenzo Bianconi return 0; 681cd53f622SLorenzo Bianconi } 682cd53f622SLorenzo Bianconi 683cd53f622SLorenzo Bianconi static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, 684cd53f622SLorenzo Bianconi struct sk_buff *skb, 685cd53f622SLorenzo Bianconi u32 hash) 68600a76783SLorenzo Bianconi { 68700a76783SLorenzo Bianconi struct airoha_flow_table_entry *e; 688cd53f622SLorenzo Bianconi struct airoha_foe_bridge br = {}; 68900a76783SLorenzo Bianconi struct airoha_foe_entry *hwe; 690cd53f622SLorenzo Bianconi bool commit_done = false; 69100a76783SLorenzo Bianconi struct hlist_node *n; 69200a76783SLorenzo Bianconi u32 index, state; 69300a76783SLorenzo Bianconi 69400a76783SLorenzo Bianconi spin_lock_bh(&ppe_lock); 69500a76783SLorenzo Bianconi 69600a76783SLorenzo Bianconi hwe = airoha_ppe_foe_get_entry(ppe, hash); 69700a76783SLorenzo Bianconi if (!hwe) 69800a76783SLorenzo Bianconi goto unlock; 69900a76783SLorenzo Bianconi 70000a76783SLorenzo Bianconi state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); 70100a76783SLorenzo Bianconi if (state == AIROHA_FOE_STATE_BIND) 70200a76783SLorenzo Bianconi goto unlock; 70300a76783SLorenzo Bianconi 70400a76783SLorenzo Bianconi index = airoha_ppe_foe_get_entry_hash(hwe); 70500a76783SLorenzo Bianconi hlist_for_each_entry_safe(e, n, &ppe->foe_flow[index], list) { 706cd53f622SLorenzo Bianconi if (e->type == FLOW_TYPE_L2_SUBFLOW) { 707cd53f622SLorenzo Bianconi state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); 708cd53f622SLorenzo Bianconi if (state != AIROHA_FOE_STATE_BIND) { 709cd53f622SLorenzo Bianconi e->hash = 0xffff; 710cd53f622SLorenzo Bianconi airoha_ppe_foe_remove_flow(ppe, e); 711cd53f622SLorenzo Bianconi } 712cd53f622SLorenzo Bianconi continue; 713cd53f622SLorenzo Bianconi } 714cd53f622SLorenzo Bianconi 715cd53f622SLorenzo Bianconi if (commit_done || !airoha_ppe_foe_compare_entry(e, hwe)) { 716cd53f622SLorenzo Bianconi e->hash = 0xffff; 717cd53f622SLorenzo Bianconi continue; 718cd53f622SLorenzo Bianconi } 719cd53f622SLorenzo Bianconi 72000a76783SLorenzo Bianconi airoha_ppe_foe_commit_entry(ppe, &e->data, hash); 721cd53f622SLorenzo Bianconi commit_done = true; 72200a76783SLorenzo Bianconi e->hash = hash; 72300a76783SLorenzo Bianconi } 724cd53f622SLorenzo Bianconi 725cd53f622SLorenzo Bianconi if (commit_done) 726cd53f622SLorenzo Bianconi goto unlock; 727cd53f622SLorenzo Bianconi 728cd53f622SLorenzo Bianconi airoha_ppe_foe_set_bridge_addrs(&br, eth_hdr(skb)); 729cd53f622SLorenzo Bianconi e = rhashtable_lookup_fast(&ppe->l2_flows, &br, 730cd53f622SLorenzo Bianconi airoha_l2_flow_table_params); 731cd53f622SLorenzo Bianconi if (e) 732cd53f622SLorenzo Bianconi airoha_ppe_foe_commit_subflow_entry(ppe, e, hash); 73300a76783SLorenzo Bianconi unlock: 73400a76783SLorenzo Bianconi spin_unlock_bh(&ppe_lock); 73500a76783SLorenzo Bianconi } 73600a76783SLorenzo Bianconi 737b4916f67SLorenzo Bianconi static int 738b4916f67SLorenzo Bianconi airoha_ppe_foe_l2_flow_commit_entry(struct airoha_ppe *ppe, 739b4916f67SLorenzo Bianconi struct airoha_flow_table_entry *e) 740b4916f67SLorenzo Bianconi { 741b4916f67SLorenzo Bianconi struct airoha_flow_table_entry *prev; 742b4916f67SLorenzo Bianconi 743b4916f67SLorenzo Bianconi e->type = FLOW_TYPE_L2; 744b4916f67SLorenzo Bianconi prev = rhashtable_lookup_get_insert_fast(&ppe->l2_flows, &e->l2_node, 745b4916f67SLorenzo Bianconi airoha_l2_flow_table_params); 746b4916f67SLorenzo Bianconi if (!prev) 747b4916f67SLorenzo Bianconi return 0; 748b4916f67SLorenzo Bianconi 749b4916f67SLorenzo Bianconi if (IS_ERR(prev)) 750b4916f67SLorenzo Bianconi return PTR_ERR(prev); 751b4916f67SLorenzo Bianconi 752b4916f67SLorenzo Bianconi return rhashtable_replace_fast(&ppe->l2_flows, &prev->l2_node, 753b4916f67SLorenzo Bianconi &e->l2_node, 754b4916f67SLorenzo Bianconi airoha_l2_flow_table_params); 755b4916f67SLorenzo Bianconi } 756b4916f67SLorenzo Bianconi 75700a76783SLorenzo Bianconi static int airoha_ppe_foe_flow_commit_entry(struct airoha_ppe *ppe, 75800a76783SLorenzo Bianconi struct airoha_flow_table_entry *e) 75900a76783SLorenzo Bianconi { 760b4916f67SLorenzo Bianconi int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, e->data.ib1); 761b4916f67SLorenzo Bianconi u32 hash; 76200a76783SLorenzo Bianconi 763b4916f67SLorenzo Bianconi if (type == PPE_PKT_TYPE_BRIDGE) 764b4916f67SLorenzo Bianconi return airoha_ppe_foe_l2_flow_commit_entry(ppe, e); 765b4916f67SLorenzo Bianconi 766b4916f67SLorenzo Bianconi hash = airoha_ppe_foe_get_entry_hash(&e->data); 767b4916f67SLorenzo Bianconi e->type = FLOW_TYPE_L4; 76800a76783SLorenzo Bianconi e->hash = 0xffff; 76900a76783SLorenzo Bianconi 77000a76783SLorenzo Bianconi spin_lock_bh(&ppe_lock); 77100a76783SLorenzo Bianconi hlist_add_head(&e->list, &ppe->foe_flow[hash]); 77200a76783SLorenzo Bianconi spin_unlock_bh(&ppe_lock); 77300a76783SLorenzo Bianconi 77400a76783SLorenzo Bianconi return 0; 77500a76783SLorenzo Bianconi } 77600a76783SLorenzo Bianconi 777b81e0f2bSLorenzo Bianconi static int airoha_ppe_get_entry_idle_time(struct airoha_ppe *ppe, u32 ib1) 778b81e0f2bSLorenzo Bianconi { 779b81e0f2bSLorenzo Bianconi u32 state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); 780b81e0f2bSLorenzo Bianconi u32 ts, ts_mask, now = airoha_ppe_get_timestamp(ppe); 781b81e0f2bSLorenzo Bianconi int idle; 782b81e0f2bSLorenzo Bianconi 783b81e0f2bSLorenzo Bianconi if (state == AIROHA_FOE_STATE_BIND) { 784b81e0f2bSLorenzo Bianconi ts = FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, ib1); 785b81e0f2bSLorenzo Bianconi ts_mask = AIROHA_FOE_IB1_BIND_TIMESTAMP; 786b81e0f2bSLorenzo Bianconi } else { 787b81e0f2bSLorenzo Bianconi ts = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, ib1); 788b81e0f2bSLorenzo Bianconi now = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, now); 789b81e0f2bSLorenzo Bianconi ts_mask = AIROHA_FOE_IB1_UNBIND_TIMESTAMP; 790b81e0f2bSLorenzo Bianconi } 791b81e0f2bSLorenzo Bianconi idle = now - ts; 792b81e0f2bSLorenzo Bianconi 793b81e0f2bSLorenzo Bianconi return idle < 0 ? idle + ts_mask + 1 : idle; 794b81e0f2bSLorenzo Bianconi } 795b81e0f2bSLorenzo Bianconi 796b81e0f2bSLorenzo Bianconi static void 797b81e0f2bSLorenzo Bianconi airoha_ppe_foe_flow_l2_entry_update(struct airoha_ppe *ppe, 798b81e0f2bSLorenzo Bianconi struct airoha_flow_table_entry *e) 799b81e0f2bSLorenzo Bianconi { 800b81e0f2bSLorenzo Bianconi int min_idle = airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); 801b81e0f2bSLorenzo Bianconi struct airoha_flow_table_entry *iter; 802b81e0f2bSLorenzo Bianconi struct hlist_node *n; 803b81e0f2bSLorenzo Bianconi 804b81e0f2bSLorenzo Bianconi lockdep_assert_held(&ppe_lock); 805b81e0f2bSLorenzo Bianconi 806b81e0f2bSLorenzo Bianconi hlist_for_each_entry_safe(iter, n, &e->l2_flows, l2_subflow_node) { 807b81e0f2bSLorenzo Bianconi struct airoha_foe_entry *hwe; 808b81e0f2bSLorenzo Bianconi u32 ib1, state; 809b81e0f2bSLorenzo Bianconi int idle; 810b81e0f2bSLorenzo Bianconi 811b81e0f2bSLorenzo Bianconi hwe = airoha_ppe_foe_get_entry(ppe, iter->hash); 812*78bd03eeSLorenzo Bianconi if (!hwe) 813*78bd03eeSLorenzo Bianconi continue; 814b81e0f2bSLorenzo Bianconi 815*78bd03eeSLorenzo Bianconi ib1 = READ_ONCE(hwe->ib1); 816b81e0f2bSLorenzo Bianconi state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); 817b81e0f2bSLorenzo Bianconi if (state != AIROHA_FOE_STATE_BIND) { 818b81e0f2bSLorenzo Bianconi iter->hash = 0xffff; 819b81e0f2bSLorenzo Bianconi airoha_ppe_foe_remove_flow(ppe, iter); 820b81e0f2bSLorenzo Bianconi continue; 821b81e0f2bSLorenzo Bianconi } 822b81e0f2bSLorenzo Bianconi 823b81e0f2bSLorenzo Bianconi idle = airoha_ppe_get_entry_idle_time(ppe, ib1); 824b81e0f2bSLorenzo Bianconi if (idle >= min_idle) 825b81e0f2bSLorenzo Bianconi continue; 826b81e0f2bSLorenzo Bianconi 827b81e0f2bSLorenzo Bianconi min_idle = idle; 828b81e0f2bSLorenzo Bianconi e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_TIMESTAMP; 829b81e0f2bSLorenzo Bianconi e->data.ib1 |= ib1 & AIROHA_FOE_IB1_BIND_TIMESTAMP; 830b81e0f2bSLorenzo Bianconi } 831b81e0f2bSLorenzo Bianconi } 832b81e0f2bSLorenzo Bianconi 833b81e0f2bSLorenzo Bianconi static void airoha_ppe_foe_flow_entry_update(struct airoha_ppe *ppe, 834b81e0f2bSLorenzo Bianconi struct airoha_flow_table_entry *e) 835b81e0f2bSLorenzo Bianconi { 836b81e0f2bSLorenzo Bianconi struct airoha_foe_entry *hwe_p, hwe = {}; 837b81e0f2bSLorenzo Bianconi 838b81e0f2bSLorenzo Bianconi spin_lock_bh(&ppe_lock); 839b81e0f2bSLorenzo Bianconi 840b81e0f2bSLorenzo Bianconi if (e->type == FLOW_TYPE_L2) { 841b81e0f2bSLorenzo Bianconi airoha_ppe_foe_flow_l2_entry_update(ppe, e); 842b81e0f2bSLorenzo Bianconi goto unlock; 843b81e0f2bSLorenzo Bianconi } 844b81e0f2bSLorenzo Bianconi 845b81e0f2bSLorenzo Bianconi if (e->hash == 0xffff) 846b81e0f2bSLorenzo Bianconi goto unlock; 847b81e0f2bSLorenzo Bianconi 848b81e0f2bSLorenzo Bianconi hwe_p = airoha_ppe_foe_get_entry(ppe, e->hash); 849b81e0f2bSLorenzo Bianconi if (!hwe_p) 850b81e0f2bSLorenzo Bianconi goto unlock; 851b81e0f2bSLorenzo Bianconi 852b81e0f2bSLorenzo Bianconi memcpy(&hwe, hwe_p, sizeof(*hwe_p)); 853b81e0f2bSLorenzo Bianconi if (!airoha_ppe_foe_compare_entry(e, &hwe)) { 854b81e0f2bSLorenzo Bianconi e->hash = 0xffff; 855b81e0f2bSLorenzo Bianconi goto unlock; 856b81e0f2bSLorenzo Bianconi } 857b81e0f2bSLorenzo Bianconi 858b81e0f2bSLorenzo Bianconi e->data.ib1 = hwe.ib1; 859b81e0f2bSLorenzo Bianconi unlock: 860b81e0f2bSLorenzo Bianconi spin_unlock_bh(&ppe_lock); 861b81e0f2bSLorenzo Bianconi } 862b81e0f2bSLorenzo Bianconi 863b81e0f2bSLorenzo Bianconi static int airoha_ppe_entry_idle_time(struct airoha_ppe *ppe, 864b81e0f2bSLorenzo Bianconi struct airoha_flow_table_entry *e) 865b81e0f2bSLorenzo Bianconi { 866b81e0f2bSLorenzo Bianconi airoha_ppe_foe_flow_entry_update(ppe, e); 867b81e0f2bSLorenzo Bianconi 868b81e0f2bSLorenzo Bianconi return airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); 869b81e0f2bSLorenzo Bianconi } 870b81e0f2bSLorenzo Bianconi 87100a76783SLorenzo Bianconi static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, 87200a76783SLorenzo Bianconi struct flow_cls_offload *f) 87300a76783SLorenzo Bianconi { 87400a76783SLorenzo Bianconi struct flow_rule *rule = flow_cls_offload_flow_rule(f); 87500a76783SLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 87600a76783SLorenzo Bianconi struct airoha_flow_table_entry *e; 87700a76783SLorenzo Bianconi struct airoha_flow_data data = {}; 87800a76783SLorenzo Bianconi struct net_device *odev = NULL; 87900a76783SLorenzo Bianconi struct flow_action_entry *act; 88000a76783SLorenzo Bianconi struct airoha_foe_entry hwe; 88100a76783SLorenzo Bianconi int err, i, offload_type; 88200a76783SLorenzo Bianconi u16 addr_type = 0; 88300a76783SLorenzo Bianconi u8 l4proto = 0; 88400a76783SLorenzo Bianconi 88500a76783SLorenzo Bianconi if (rhashtable_lookup(ð->flow_table, &f->cookie, 88600a76783SLorenzo Bianconi airoha_flow_table_params)) 88700a76783SLorenzo Bianconi return -EEXIST; 88800a76783SLorenzo Bianconi 88900a76783SLorenzo Bianconi if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) 89000a76783SLorenzo Bianconi return -EOPNOTSUPP; 89100a76783SLorenzo Bianconi 89200a76783SLorenzo Bianconi if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { 89300a76783SLorenzo Bianconi struct flow_match_control match; 89400a76783SLorenzo Bianconi 89500a76783SLorenzo Bianconi flow_rule_match_control(rule, &match); 89600a76783SLorenzo Bianconi addr_type = match.key->addr_type; 89700a76783SLorenzo Bianconi if (flow_rule_has_control_flags(match.mask->flags, 89800a76783SLorenzo Bianconi f->common.extack)) 89900a76783SLorenzo Bianconi return -EOPNOTSUPP; 90000a76783SLorenzo Bianconi } else { 90100a76783SLorenzo Bianconi return -EOPNOTSUPP; 90200a76783SLorenzo Bianconi } 90300a76783SLorenzo Bianconi 90400a76783SLorenzo Bianconi if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { 90500a76783SLorenzo Bianconi struct flow_match_basic match; 90600a76783SLorenzo Bianconi 90700a76783SLorenzo Bianconi flow_rule_match_basic(rule, &match); 90800a76783SLorenzo Bianconi l4proto = match.key->ip_proto; 90900a76783SLorenzo Bianconi } else { 91000a76783SLorenzo Bianconi return -EOPNOTSUPP; 91100a76783SLorenzo Bianconi } 91200a76783SLorenzo Bianconi 91300a76783SLorenzo Bianconi switch (addr_type) { 91400a76783SLorenzo Bianconi case 0: 91500a76783SLorenzo Bianconi offload_type = PPE_PKT_TYPE_BRIDGE; 91600a76783SLorenzo Bianconi if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 91700a76783SLorenzo Bianconi struct flow_match_eth_addrs match; 91800a76783SLorenzo Bianconi 91900a76783SLorenzo Bianconi flow_rule_match_eth_addrs(rule, &match); 92000a76783SLorenzo Bianconi memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN); 92100a76783SLorenzo Bianconi memcpy(data.eth.h_source, match.key->src, ETH_ALEN); 92200a76783SLorenzo Bianconi } else { 92300a76783SLorenzo Bianconi return -EOPNOTSUPP; 92400a76783SLorenzo Bianconi } 92500a76783SLorenzo Bianconi break; 92600a76783SLorenzo Bianconi case FLOW_DISSECTOR_KEY_IPV4_ADDRS: 92700a76783SLorenzo Bianconi offload_type = PPE_PKT_TYPE_IPV4_HNAPT; 92800a76783SLorenzo Bianconi break; 92900a76783SLorenzo Bianconi case FLOW_DISSECTOR_KEY_IPV6_ADDRS: 93000a76783SLorenzo Bianconi offload_type = PPE_PKT_TYPE_IPV6_ROUTE_5T; 93100a76783SLorenzo Bianconi break; 93200a76783SLorenzo Bianconi default: 93300a76783SLorenzo Bianconi return -EOPNOTSUPP; 93400a76783SLorenzo Bianconi } 93500a76783SLorenzo Bianconi 93600a76783SLorenzo Bianconi flow_action_for_each(i, act, &rule->action) { 93700a76783SLorenzo Bianconi switch (act->id) { 93800a76783SLorenzo Bianconi case FLOW_ACTION_MANGLE: 93900a76783SLorenzo Bianconi if (offload_type == PPE_PKT_TYPE_BRIDGE) 94000a76783SLorenzo Bianconi return -EOPNOTSUPP; 94100a76783SLorenzo Bianconi 94200a76783SLorenzo Bianconi if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH) 94300a76783SLorenzo Bianconi airoha_ppe_flow_mangle_eth(act, &data.eth); 94400a76783SLorenzo Bianconi break; 94500a76783SLorenzo Bianconi case FLOW_ACTION_REDIRECT: 94600a76783SLorenzo Bianconi odev = act->dev; 94700a76783SLorenzo Bianconi break; 94800a76783SLorenzo Bianconi case FLOW_ACTION_CSUM: 94900a76783SLorenzo Bianconi break; 95000a76783SLorenzo Bianconi case FLOW_ACTION_VLAN_PUSH: 95100a76783SLorenzo Bianconi if (data.vlan.num == 2 || 95200a76783SLorenzo Bianconi act->vlan.proto != htons(ETH_P_8021Q)) 95300a76783SLorenzo Bianconi return -EOPNOTSUPP; 95400a76783SLorenzo Bianconi 95500a76783SLorenzo Bianconi data.vlan.hdr[data.vlan.num].id = act->vlan.vid; 95600a76783SLorenzo Bianconi data.vlan.hdr[data.vlan.num].proto = act->vlan.proto; 95700a76783SLorenzo Bianconi data.vlan.num++; 95800a76783SLorenzo Bianconi break; 95900a76783SLorenzo Bianconi case FLOW_ACTION_VLAN_POP: 96000a76783SLorenzo Bianconi break; 96100a76783SLorenzo Bianconi case FLOW_ACTION_PPPOE_PUSH: 96200a76783SLorenzo Bianconi break; 96300a76783SLorenzo Bianconi default: 96400a76783SLorenzo Bianconi return -EOPNOTSUPP; 96500a76783SLorenzo Bianconi } 96600a76783SLorenzo Bianconi } 96700a76783SLorenzo Bianconi 96800a76783SLorenzo Bianconi if (!is_valid_ether_addr(data.eth.h_source) || 96900a76783SLorenzo Bianconi !is_valid_ether_addr(data.eth.h_dest)) 97000a76783SLorenzo Bianconi return -EINVAL; 97100a76783SLorenzo Bianconi 97209bccf56SLorenzo Bianconi err = airoha_ppe_foe_entry_prepare(eth, &hwe, odev, offload_type, 97300a76783SLorenzo Bianconi &data, l4proto); 97400a76783SLorenzo Bianconi if (err) 97500a76783SLorenzo Bianconi return err; 97600a76783SLorenzo Bianconi 97700a76783SLorenzo Bianconi if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { 97800a76783SLorenzo Bianconi struct flow_match_ports ports; 97900a76783SLorenzo Bianconi 98000a76783SLorenzo Bianconi if (offload_type == PPE_PKT_TYPE_BRIDGE) 98100a76783SLorenzo Bianconi return -EOPNOTSUPP; 98200a76783SLorenzo Bianconi 98300a76783SLorenzo Bianconi flow_rule_match_ports(rule, &ports); 98400a76783SLorenzo Bianconi data.src_port = ports.key->src; 98500a76783SLorenzo Bianconi data.dst_port = ports.key->dst; 98600a76783SLorenzo Bianconi } else if (offload_type != PPE_PKT_TYPE_BRIDGE) { 98700a76783SLorenzo Bianconi return -EOPNOTSUPP; 98800a76783SLorenzo Bianconi } 98900a76783SLorenzo Bianconi 99000a76783SLorenzo Bianconi if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 99100a76783SLorenzo Bianconi struct flow_match_ipv4_addrs addrs; 99200a76783SLorenzo Bianconi 99300a76783SLorenzo Bianconi flow_rule_match_ipv4_addrs(rule, &addrs); 99400a76783SLorenzo Bianconi data.v4.src_addr = addrs.key->src; 99500a76783SLorenzo Bianconi data.v4.dst_addr = addrs.key->dst; 99600a76783SLorenzo Bianconi airoha_ppe_foe_entry_set_ipv4_tuple(&hwe, &data, false); 99700a76783SLorenzo Bianconi } 99800a76783SLorenzo Bianconi 99900a76783SLorenzo Bianconi if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { 100000a76783SLorenzo Bianconi struct flow_match_ipv6_addrs addrs; 100100a76783SLorenzo Bianconi 100200a76783SLorenzo Bianconi flow_rule_match_ipv6_addrs(rule, &addrs); 100300a76783SLorenzo Bianconi 100400a76783SLorenzo Bianconi data.v6.src_addr = addrs.key->src; 100500a76783SLorenzo Bianconi data.v6.dst_addr = addrs.key->dst; 100600a76783SLorenzo Bianconi airoha_ppe_foe_entry_set_ipv6_tuple(&hwe, &data); 100700a76783SLorenzo Bianconi } 100800a76783SLorenzo Bianconi 100900a76783SLorenzo Bianconi flow_action_for_each(i, act, &rule->action) { 101000a76783SLorenzo Bianconi if (act->id != FLOW_ACTION_MANGLE) 101100a76783SLorenzo Bianconi continue; 101200a76783SLorenzo Bianconi 101300a76783SLorenzo Bianconi if (offload_type == PPE_PKT_TYPE_BRIDGE) 101400a76783SLorenzo Bianconi return -EOPNOTSUPP; 101500a76783SLorenzo Bianconi 101600a76783SLorenzo Bianconi switch (act->mangle.htype) { 101700a76783SLorenzo Bianconi case FLOW_ACT_MANGLE_HDR_TYPE_TCP: 101800a76783SLorenzo Bianconi case FLOW_ACT_MANGLE_HDR_TYPE_UDP: 101900a76783SLorenzo Bianconi err = airoha_ppe_flow_mangle_ports(act, &data); 102000a76783SLorenzo Bianconi break; 102100a76783SLorenzo Bianconi case FLOW_ACT_MANGLE_HDR_TYPE_IP4: 102200a76783SLorenzo Bianconi err = airoha_ppe_flow_mangle_ipv4(act, &data); 102300a76783SLorenzo Bianconi break; 102400a76783SLorenzo Bianconi case FLOW_ACT_MANGLE_HDR_TYPE_ETH: 102500a76783SLorenzo Bianconi /* handled earlier */ 102600a76783SLorenzo Bianconi break; 102700a76783SLorenzo Bianconi default: 102800a76783SLorenzo Bianconi return -EOPNOTSUPP; 102900a76783SLorenzo Bianconi } 103000a76783SLorenzo Bianconi 103100a76783SLorenzo Bianconi if (err) 103200a76783SLorenzo Bianconi return err; 103300a76783SLorenzo Bianconi } 103400a76783SLorenzo Bianconi 103500a76783SLorenzo Bianconi if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 103600a76783SLorenzo Bianconi err = airoha_ppe_foe_entry_set_ipv4_tuple(&hwe, &data, true); 103700a76783SLorenzo Bianconi if (err) 103800a76783SLorenzo Bianconi return err; 103900a76783SLorenzo Bianconi } 104000a76783SLorenzo Bianconi 104100a76783SLorenzo Bianconi e = kzalloc(sizeof(*e), GFP_KERNEL); 104200a76783SLorenzo Bianconi if (!e) 104300a76783SLorenzo Bianconi return -ENOMEM; 104400a76783SLorenzo Bianconi 104500a76783SLorenzo Bianconi e->cookie = f->cookie; 104600a76783SLorenzo Bianconi memcpy(&e->data, &hwe, sizeof(e->data)); 104700a76783SLorenzo Bianconi 104800a76783SLorenzo Bianconi err = airoha_ppe_foe_flow_commit_entry(eth->ppe, e); 104900a76783SLorenzo Bianconi if (err) 105000a76783SLorenzo Bianconi goto free_entry; 105100a76783SLorenzo Bianconi 105200a76783SLorenzo Bianconi err = rhashtable_insert_fast(ð->flow_table, &e->node, 105300a76783SLorenzo Bianconi airoha_flow_table_params); 105400a76783SLorenzo Bianconi if (err < 0) 105500a76783SLorenzo Bianconi goto remove_foe_entry; 105600a76783SLorenzo Bianconi 105700a76783SLorenzo Bianconi return 0; 105800a76783SLorenzo Bianconi 105900a76783SLorenzo Bianconi remove_foe_entry: 106000a76783SLorenzo Bianconi airoha_ppe_foe_flow_remove_entry(eth->ppe, e); 106100a76783SLorenzo Bianconi free_entry: 106200a76783SLorenzo Bianconi kfree(e); 106300a76783SLorenzo Bianconi 106400a76783SLorenzo Bianconi return err; 106500a76783SLorenzo Bianconi } 106600a76783SLorenzo Bianconi 106700a76783SLorenzo Bianconi static int airoha_ppe_flow_offload_destroy(struct airoha_gdm_port *port, 106800a76783SLorenzo Bianconi struct flow_cls_offload *f) 106900a76783SLorenzo Bianconi { 107000a76783SLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 107100a76783SLorenzo Bianconi struct airoha_flow_table_entry *e; 107200a76783SLorenzo Bianconi 107300a76783SLorenzo Bianconi e = rhashtable_lookup(ð->flow_table, &f->cookie, 107400a76783SLorenzo Bianconi airoha_flow_table_params); 107500a76783SLorenzo Bianconi if (!e) 107600a76783SLorenzo Bianconi return -ENOENT; 107700a76783SLorenzo Bianconi 107800a76783SLorenzo Bianconi airoha_ppe_foe_flow_remove_entry(eth->ppe, e); 107900a76783SLorenzo Bianconi rhashtable_remove_fast(ð->flow_table, &e->node, 108000a76783SLorenzo Bianconi airoha_flow_table_params); 108100a76783SLorenzo Bianconi kfree(e); 108200a76783SLorenzo Bianconi 108300a76783SLorenzo Bianconi return 0; 108400a76783SLorenzo Bianconi } 108500a76783SLorenzo Bianconi 1086b81e0f2bSLorenzo Bianconi void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, 1087b81e0f2bSLorenzo Bianconi struct airoha_foe_stats64 *stats) 1088b81e0f2bSLorenzo Bianconi { 1089b81e0f2bSLorenzo Bianconi u32 index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); 1090b81e0f2bSLorenzo Bianconi struct airoha_eth *eth = ppe->eth; 1091b81e0f2bSLorenzo Bianconi struct airoha_npu *npu; 1092b81e0f2bSLorenzo Bianconi 1093b81e0f2bSLorenzo Bianconi if (index >= PPE_STATS_NUM_ENTRIES) 1094b81e0f2bSLorenzo Bianconi return; 1095b81e0f2bSLorenzo Bianconi 1096b81e0f2bSLorenzo Bianconi rcu_read_lock(); 1097b81e0f2bSLorenzo Bianconi 1098b81e0f2bSLorenzo Bianconi npu = rcu_dereference(eth->npu); 1099b81e0f2bSLorenzo Bianconi if (npu) { 1100b81e0f2bSLorenzo Bianconi u64 packets = ppe->foe_stats[index].packets; 1101b81e0f2bSLorenzo Bianconi u64 bytes = ppe->foe_stats[index].bytes; 1102b81e0f2bSLorenzo Bianconi struct airoha_foe_stats npu_stats; 1103b81e0f2bSLorenzo Bianconi 1104b81e0f2bSLorenzo Bianconi memcpy_fromio(&npu_stats, &npu->stats[index], 1105b81e0f2bSLorenzo Bianconi sizeof(*npu->stats)); 1106b81e0f2bSLorenzo Bianconi stats->packets = packets << 32 | npu_stats.packets; 1107b81e0f2bSLorenzo Bianconi stats->bytes = bytes << 32 | npu_stats.bytes; 1108b81e0f2bSLorenzo Bianconi } 1109b81e0f2bSLorenzo Bianconi 1110b81e0f2bSLorenzo Bianconi rcu_read_unlock(); 1111b81e0f2bSLorenzo Bianconi } 1112b81e0f2bSLorenzo Bianconi 1113b81e0f2bSLorenzo Bianconi static int airoha_ppe_flow_offload_stats(struct airoha_gdm_port *port, 1114b81e0f2bSLorenzo Bianconi struct flow_cls_offload *f) 1115b81e0f2bSLorenzo Bianconi { 1116b81e0f2bSLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 1117b81e0f2bSLorenzo Bianconi struct airoha_flow_table_entry *e; 1118b81e0f2bSLorenzo Bianconi u32 idle; 1119b81e0f2bSLorenzo Bianconi 1120b81e0f2bSLorenzo Bianconi e = rhashtable_lookup(ð->flow_table, &f->cookie, 1121b81e0f2bSLorenzo Bianconi airoha_flow_table_params); 1122b81e0f2bSLorenzo Bianconi if (!e) 1123b81e0f2bSLorenzo Bianconi return -ENOENT; 1124b81e0f2bSLorenzo Bianconi 1125b81e0f2bSLorenzo Bianconi idle = airoha_ppe_entry_idle_time(eth->ppe, e); 1126b81e0f2bSLorenzo Bianconi f->stats.lastused = jiffies - idle * HZ; 1127b81e0f2bSLorenzo Bianconi 1128b81e0f2bSLorenzo Bianconi if (e->hash != 0xffff) { 1129b81e0f2bSLorenzo Bianconi struct airoha_foe_stats64 stats = {}; 1130b81e0f2bSLorenzo Bianconi 1131b81e0f2bSLorenzo Bianconi airoha_ppe_foe_entry_get_stats(eth->ppe, e->hash, &stats); 1132b81e0f2bSLorenzo Bianconi f->stats.pkts += (stats.packets - e->stats.packets); 1133b81e0f2bSLorenzo Bianconi f->stats.bytes += (stats.bytes - e->stats.bytes); 1134b81e0f2bSLorenzo Bianconi e->stats = stats; 1135b81e0f2bSLorenzo Bianconi } 1136b81e0f2bSLorenzo Bianconi 1137b81e0f2bSLorenzo Bianconi return 0; 1138b81e0f2bSLorenzo Bianconi } 1139b81e0f2bSLorenzo Bianconi 114000a76783SLorenzo Bianconi static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port, 114100a76783SLorenzo Bianconi struct flow_cls_offload *f) 114200a76783SLorenzo Bianconi { 114300a76783SLorenzo Bianconi switch (f->command) { 114400a76783SLorenzo Bianconi case FLOW_CLS_REPLACE: 114500a76783SLorenzo Bianconi return airoha_ppe_flow_offload_replace(port, f); 114600a76783SLorenzo Bianconi case FLOW_CLS_DESTROY: 114700a76783SLorenzo Bianconi return airoha_ppe_flow_offload_destroy(port, f); 1148b81e0f2bSLorenzo Bianconi case FLOW_CLS_STATS: 1149b81e0f2bSLorenzo Bianconi return airoha_ppe_flow_offload_stats(port, f); 115000a76783SLorenzo Bianconi default: 115100a76783SLorenzo Bianconi break; 115200a76783SLorenzo Bianconi } 115300a76783SLorenzo Bianconi 115400a76783SLorenzo Bianconi return -EOPNOTSUPP; 115500a76783SLorenzo Bianconi } 115600a76783SLorenzo Bianconi 115700a76783SLorenzo Bianconi static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe, 115800a76783SLorenzo Bianconi struct airoha_npu *npu) 115900a76783SLorenzo Bianconi { 116000a76783SLorenzo Bianconi int i, sram_num_entries = PPE_SRAM_NUM_ENTRIES; 116100a76783SLorenzo Bianconi struct airoha_foe_entry *hwe = ppe->foe; 116200a76783SLorenzo Bianconi 116300a76783SLorenzo Bianconi if (airoha_ppe2_is_enabled(ppe->eth)) 116400a76783SLorenzo Bianconi sram_num_entries = sram_num_entries / 2; 116500a76783SLorenzo Bianconi 116600a76783SLorenzo Bianconi for (i = 0; i < sram_num_entries; i++) 116700a76783SLorenzo Bianconi memset(&hwe[i], 0, sizeof(*hwe)); 116800a76783SLorenzo Bianconi 116900a76783SLorenzo Bianconi return npu->ops.ppe_flush_sram_entries(npu, ppe->foe_dma, 117000a76783SLorenzo Bianconi PPE_SRAM_NUM_ENTRIES); 117100a76783SLorenzo Bianconi } 117200a76783SLorenzo Bianconi 117300a76783SLorenzo Bianconi static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) 117400a76783SLorenzo Bianconi { 1175b81e0f2bSLorenzo Bianconi struct airoha_npu *npu = airoha_npu_get(eth->dev, 1176b81e0f2bSLorenzo Bianconi ð->ppe->foe_stats_dma); 117700a76783SLorenzo Bianconi 117800a76783SLorenzo Bianconi if (IS_ERR(npu)) { 117900a76783SLorenzo Bianconi request_module("airoha-npu"); 1180b81e0f2bSLorenzo Bianconi npu = airoha_npu_get(eth->dev, ð->ppe->foe_stats_dma); 118100a76783SLorenzo Bianconi } 118200a76783SLorenzo Bianconi 118300a76783SLorenzo Bianconi return npu; 118400a76783SLorenzo Bianconi } 118500a76783SLorenzo Bianconi 118600a76783SLorenzo Bianconi static int airoha_ppe_offload_setup(struct airoha_eth *eth) 118700a76783SLorenzo Bianconi { 118800a76783SLorenzo Bianconi struct airoha_npu *npu = airoha_ppe_npu_get(eth); 118900a76783SLorenzo Bianconi int err; 119000a76783SLorenzo Bianconi 119100a76783SLorenzo Bianconi if (IS_ERR(npu)) 119200a76783SLorenzo Bianconi return PTR_ERR(npu); 119300a76783SLorenzo Bianconi 119400a76783SLorenzo Bianconi err = npu->ops.ppe_init(npu); 119500a76783SLorenzo Bianconi if (err) 119600a76783SLorenzo Bianconi goto error_npu_put; 119700a76783SLorenzo Bianconi 119800a76783SLorenzo Bianconi airoha_ppe_hw_init(eth->ppe); 119900a76783SLorenzo Bianconi err = airoha_ppe_flush_sram_entries(eth->ppe, npu); 120000a76783SLorenzo Bianconi if (err) 120100a76783SLorenzo Bianconi goto error_npu_put; 120200a76783SLorenzo Bianconi 1203b81e0f2bSLorenzo Bianconi airoha_ppe_foe_flow_stats_reset(eth->ppe, npu); 1204b81e0f2bSLorenzo Bianconi 120500a76783SLorenzo Bianconi rcu_assign_pointer(eth->npu, npu); 120600a76783SLorenzo Bianconi synchronize_rcu(); 120700a76783SLorenzo Bianconi 120800a76783SLorenzo Bianconi return 0; 120900a76783SLorenzo Bianconi 121000a76783SLorenzo Bianconi error_npu_put: 121100a76783SLorenzo Bianconi airoha_npu_put(npu); 121200a76783SLorenzo Bianconi 121300a76783SLorenzo Bianconi return err; 121400a76783SLorenzo Bianconi } 121500a76783SLorenzo Bianconi 1216df8398fbSLorenzo Bianconi int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data) 121700a76783SLorenzo Bianconi { 121800a76783SLorenzo Bianconi struct airoha_gdm_port *port = netdev_priv(dev); 1219df8398fbSLorenzo Bianconi struct flow_cls_offload *cls = type_data; 122000a76783SLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 122100a76783SLorenzo Bianconi int err = 0; 122200a76783SLorenzo Bianconi 122300a76783SLorenzo Bianconi mutex_lock(&flow_offload_mutex); 122400a76783SLorenzo Bianconi 122500a76783SLorenzo Bianconi if (!eth->npu) 122600a76783SLorenzo Bianconi err = airoha_ppe_offload_setup(eth); 122700a76783SLorenzo Bianconi if (!err) 122800a76783SLorenzo Bianconi err = airoha_ppe_flow_offload_cmd(port, cls); 122900a76783SLorenzo Bianconi 123000a76783SLorenzo Bianconi mutex_unlock(&flow_offload_mutex); 123100a76783SLorenzo Bianconi 123200a76783SLorenzo Bianconi return err; 123300a76783SLorenzo Bianconi } 123400a76783SLorenzo Bianconi 1235cd53f622SLorenzo Bianconi void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, 1236cd53f622SLorenzo Bianconi u16 hash) 123700a76783SLorenzo Bianconi { 123800a76783SLorenzo Bianconi u16 now, diff; 123900a76783SLorenzo Bianconi 124000a76783SLorenzo Bianconi if (hash > PPE_HASH_MASK) 124100a76783SLorenzo Bianconi return; 124200a76783SLorenzo Bianconi 124300a76783SLorenzo Bianconi now = (u16)jiffies; 124400a76783SLorenzo Bianconi diff = now - ppe->foe_check_time[hash]; 124500a76783SLorenzo Bianconi if (diff < HZ / 10) 124600a76783SLorenzo Bianconi return; 124700a76783SLorenzo Bianconi 124800a76783SLorenzo Bianconi ppe->foe_check_time[hash] = now; 1249cd53f622SLorenzo Bianconi airoha_ppe_foe_insert_entry(ppe, skb, hash); 125000a76783SLorenzo Bianconi } 125100a76783SLorenzo Bianconi 1252a869d3a5SLorenzo Bianconi void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port) 1253a869d3a5SLorenzo Bianconi { 1254a869d3a5SLorenzo Bianconi struct airoha_eth *eth = port->qdma->eth; 1255a869d3a5SLorenzo Bianconi struct net_device *dev = port->dev; 1256a869d3a5SLorenzo Bianconi const u8 *addr = dev->dev_addr; 1257a869d3a5SLorenzo Bianconi u32 val; 1258a869d3a5SLorenzo Bianconi 1259a869d3a5SLorenzo Bianconi val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; 1260a869d3a5SLorenzo Bianconi airoha_fe_wr(eth, REG_UPDMEM_DATA(0), val); 1261a869d3a5SLorenzo Bianconi airoha_fe_wr(eth, REG_UPDMEM_CTRL(0), 1262a869d3a5SLorenzo Bianconi FIELD_PREP(PPE_UPDMEM_ADDR_MASK, port->id) | 1263a869d3a5SLorenzo Bianconi PPE_UPDMEM_WR_MASK | PPE_UPDMEM_REQ_MASK); 1264a869d3a5SLorenzo Bianconi 1265a869d3a5SLorenzo Bianconi val = (addr[0] << 8) | addr[1]; 1266a869d3a5SLorenzo Bianconi airoha_fe_wr(eth, REG_UPDMEM_DATA(0), val); 1267a869d3a5SLorenzo Bianconi airoha_fe_wr(eth, REG_UPDMEM_CTRL(0), 1268a869d3a5SLorenzo Bianconi FIELD_PREP(PPE_UPDMEM_ADDR_MASK, port->id) | 1269a869d3a5SLorenzo Bianconi FIELD_PREP(PPE_UPDMEM_OFFSET_MASK, 1) | 1270a869d3a5SLorenzo Bianconi PPE_UPDMEM_WR_MASK | PPE_UPDMEM_REQ_MASK); 1271a869d3a5SLorenzo Bianconi } 1272a869d3a5SLorenzo Bianconi 127300a76783SLorenzo Bianconi int airoha_ppe_init(struct airoha_eth *eth) 127400a76783SLorenzo Bianconi { 127500a76783SLorenzo Bianconi struct airoha_ppe *ppe; 12763fe15c64SLorenzo Bianconi int foe_size, err; 127700a76783SLorenzo Bianconi 127800a76783SLorenzo Bianconi ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL); 127900a76783SLorenzo Bianconi if (!ppe) 128000a76783SLorenzo Bianconi return -ENOMEM; 128100a76783SLorenzo Bianconi 128200a76783SLorenzo Bianconi foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry); 128300a76783SLorenzo Bianconi ppe->foe = dmam_alloc_coherent(eth->dev, foe_size, &ppe->foe_dma, 128400a76783SLorenzo Bianconi GFP_KERNEL); 128500a76783SLorenzo Bianconi if (!ppe->foe) 128600a76783SLorenzo Bianconi return -ENOMEM; 128700a76783SLorenzo Bianconi 128800a76783SLorenzo Bianconi ppe->eth = eth; 128900a76783SLorenzo Bianconi eth->ppe = ppe; 129000a76783SLorenzo Bianconi 129100a76783SLorenzo Bianconi ppe->foe_flow = devm_kzalloc(eth->dev, 129200a76783SLorenzo Bianconi PPE_NUM_ENTRIES * sizeof(*ppe->foe_flow), 129300a76783SLorenzo Bianconi GFP_KERNEL); 129400a76783SLorenzo Bianconi if (!ppe->foe_flow) 129500a76783SLorenzo Bianconi return -ENOMEM; 129600a76783SLorenzo Bianconi 1297b81e0f2bSLorenzo Bianconi foe_size = PPE_STATS_NUM_ENTRIES * sizeof(*ppe->foe_stats); 1298b81e0f2bSLorenzo Bianconi if (foe_size) { 1299b81e0f2bSLorenzo Bianconi ppe->foe_stats = dmam_alloc_coherent(eth->dev, foe_size, 1300b81e0f2bSLorenzo Bianconi &ppe->foe_stats_dma, 1301b81e0f2bSLorenzo Bianconi GFP_KERNEL); 1302b81e0f2bSLorenzo Bianconi if (!ppe->foe_stats) 1303b81e0f2bSLorenzo Bianconi return -ENOMEM; 1304b81e0f2bSLorenzo Bianconi } 1305b81e0f2bSLorenzo Bianconi 13063fe15c64SLorenzo Bianconi err = rhashtable_init(ð->flow_table, &airoha_flow_table_params); 13073fe15c64SLorenzo Bianconi if (err) 13083fe15c64SLorenzo Bianconi return err; 13093fe15c64SLorenzo Bianconi 1310b4916f67SLorenzo Bianconi err = rhashtable_init(&ppe->l2_flows, &airoha_l2_flow_table_params); 1311b4916f67SLorenzo Bianconi if (err) 1312b4916f67SLorenzo Bianconi goto error_flow_table_destroy; 1313b4916f67SLorenzo Bianconi 13143fe15c64SLorenzo Bianconi err = airoha_ppe_debugfs_init(ppe); 13153fe15c64SLorenzo Bianconi if (err) 1316b4916f67SLorenzo Bianconi goto error_l2_flow_table_destroy; 1317b4916f67SLorenzo Bianconi 1318b4916f67SLorenzo Bianconi return 0; 1319b4916f67SLorenzo Bianconi 1320b4916f67SLorenzo Bianconi error_l2_flow_table_destroy: 1321b4916f67SLorenzo Bianconi rhashtable_destroy(&ppe->l2_flows); 1322b4916f67SLorenzo Bianconi error_flow_table_destroy: 13233fe15c64SLorenzo Bianconi rhashtable_destroy(ð->flow_table); 13243fe15c64SLorenzo Bianconi 13253fe15c64SLorenzo Bianconi return err; 132600a76783SLorenzo Bianconi } 132700a76783SLorenzo Bianconi 132800a76783SLorenzo Bianconi void airoha_ppe_deinit(struct airoha_eth *eth) 132900a76783SLorenzo Bianconi { 133000a76783SLorenzo Bianconi struct airoha_npu *npu; 133100a76783SLorenzo Bianconi 133200a76783SLorenzo Bianconi rcu_read_lock(); 133300a76783SLorenzo Bianconi npu = rcu_dereference(eth->npu); 133400a76783SLorenzo Bianconi if (npu) { 133500a76783SLorenzo Bianconi npu->ops.ppe_deinit(npu); 133600a76783SLorenzo Bianconi airoha_npu_put(npu); 133700a76783SLorenzo Bianconi } 133800a76783SLorenzo Bianconi rcu_read_unlock(); 133900a76783SLorenzo Bianconi 1340b4916f67SLorenzo Bianconi rhashtable_destroy(ð->ppe->l2_flows); 134100a76783SLorenzo Bianconi rhashtable_destroy(ð->flow_table); 13423fe15c64SLorenzo Bianconi debugfs_remove(eth->ppe->debugfs_dir); 134300a76783SLorenzo Bianconi } 1344