18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci/* Copyright 2019 NXP */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include "enetc.h" 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 78c2ecf20Sopenharmony_ci#include <linux/math64.h> 88c2ecf20Sopenharmony_ci#include <linux/refcount.h> 98c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 108c2ecf20Sopenharmony_ci#include <net/tc_act/tc_gate.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic u16 enetc_get_max_gcl_len(struct enetc_hw *hw) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci return enetc_rd(hw, ENETC_QBV_PTGCAPR_OFFSET) 158c2ecf20Sopenharmony_ci & ENETC_QBV_MAX_GCL_LEN_MASK; 168c2ecf20Sopenharmony_ci} 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_civoid enetc_sched_speed_set(struct enetc_ndev_priv *priv, int speed) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct enetc_hw *hw = &priv->si->hw; 218c2ecf20Sopenharmony_ci u32 old_speed = priv->speed; 228c2ecf20Sopenharmony_ci u32 pspeed, tmp; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci if (speed == old_speed) 258c2ecf20Sopenharmony_ci return; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci switch (speed) { 288c2ecf20Sopenharmony_ci case SPEED_1000: 298c2ecf20Sopenharmony_ci pspeed = ENETC_PMR_PSPEED_1000M; 308c2ecf20Sopenharmony_ci break; 318c2ecf20Sopenharmony_ci case SPEED_2500: 328c2ecf20Sopenharmony_ci pspeed = ENETC_PMR_PSPEED_2500M; 338c2ecf20Sopenharmony_ci break; 348c2ecf20Sopenharmony_ci case SPEED_100: 358c2ecf20Sopenharmony_ci pspeed = ENETC_PMR_PSPEED_100M; 368c2ecf20Sopenharmony_ci break; 378c2ecf20Sopenharmony_ci case SPEED_10: 388c2ecf20Sopenharmony_ci default: 398c2ecf20Sopenharmony_ci pspeed = ENETC_PMR_PSPEED_10M; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci priv->speed = speed; 438c2ecf20Sopenharmony_ci tmp = enetc_port_rd(hw, ENETC_PMR); 448c2ecf20Sopenharmony_ci enetc_port_wr(hw, ENETC_PMR, (tmp & ~ENETC_PMR_PSPEED_MASK) | pspeed); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int enetc_setup_taprio(struct net_device *ndev, 488c2ecf20Sopenharmony_ci struct tc_taprio_qopt_offload *admin_conf) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct enetc_ndev_priv *priv = netdev_priv(ndev); 518c2ecf20Sopenharmony_ci struct enetc_hw *hw = &priv->si->hw; 528c2ecf20Sopenharmony_ci struct enetc_cbd cbd = {.cmd = 0}; 538c2ecf20Sopenharmony_ci struct tgs_gcl_conf *gcl_config; 548c2ecf20Sopenharmony_ci struct tgs_gcl_data *gcl_data; 558c2ecf20Sopenharmony_ci struct gce *gce; 568c2ecf20Sopenharmony_ci dma_addr_t dma; 578c2ecf20Sopenharmony_ci u16 data_size; 588c2ecf20Sopenharmony_ci u16 gcl_len; 598c2ecf20Sopenharmony_ci u32 tge; 608c2ecf20Sopenharmony_ci int err; 618c2ecf20Sopenharmony_ci int i; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (admin_conf->num_entries > enetc_get_max_gcl_len(hw)) 648c2ecf20Sopenharmony_ci return -EINVAL; 658c2ecf20Sopenharmony_ci gcl_len = admin_conf->num_entries; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci tge = enetc_rd(hw, ENETC_QBV_PTGCR_OFFSET); 688c2ecf20Sopenharmony_ci if (!admin_conf->enable) { 698c2ecf20Sopenharmony_ci enetc_wr(hw, ENETC_QBV_PTGCR_OFFSET, tge & ~ENETC_QBV_TGE); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci priv->active_offloads &= ~ENETC_F_QBV; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (admin_conf->cycle_time > U32_MAX || 778c2ecf20Sopenharmony_ci admin_conf->cycle_time_extension > U32_MAX) 788c2ecf20Sopenharmony_ci return -EINVAL; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* Configure the (administrative) gate control list using the 818c2ecf20Sopenharmony_ci * control BD descriptor. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci gcl_config = &cbd.gcl_conf; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci data_size = struct_size(gcl_data, entry, gcl_len); 868c2ecf20Sopenharmony_ci gcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); 878c2ecf20Sopenharmony_ci if (!gcl_data) 888c2ecf20Sopenharmony_ci return -ENOMEM; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci gce = (struct gce *)(gcl_data + 1); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Set all gates open as default */ 938c2ecf20Sopenharmony_ci gcl_config->atc = 0xff; 948c2ecf20Sopenharmony_ci gcl_config->acl_len = cpu_to_le16(gcl_len); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci gcl_data->btl = cpu_to_le32(lower_32_bits(admin_conf->base_time)); 978c2ecf20Sopenharmony_ci gcl_data->bth = cpu_to_le32(upper_32_bits(admin_conf->base_time)); 988c2ecf20Sopenharmony_ci gcl_data->ct = cpu_to_le32(admin_conf->cycle_time); 998c2ecf20Sopenharmony_ci gcl_data->cte = cpu_to_le32(admin_conf->cycle_time_extension); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci for (i = 0; i < gcl_len; i++) { 1028c2ecf20Sopenharmony_ci struct tc_taprio_sched_entry *temp_entry; 1038c2ecf20Sopenharmony_ci struct gce *temp_gce = gce + i; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci temp_entry = &admin_conf->entries[i]; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci temp_gce->gate = (u8)temp_entry->gate_mask; 1088c2ecf20Sopenharmony_ci temp_gce->period = cpu_to_le32(temp_entry->interval); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci cbd.length = cpu_to_le16(data_size); 1128c2ecf20Sopenharmony_ci cbd.status_flags = 0; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci dma = dma_map_single(&priv->si->pdev->dev, gcl_data, 1158c2ecf20Sopenharmony_ci data_size, DMA_TO_DEVICE); 1168c2ecf20Sopenharmony_ci if (dma_mapping_error(&priv->si->pdev->dev, dma)) { 1178c2ecf20Sopenharmony_ci netdev_err(priv->si->ndev, "DMA mapping failed!\n"); 1188c2ecf20Sopenharmony_ci kfree(gcl_data); 1198c2ecf20Sopenharmony_ci return -ENOMEM; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci cbd.addr[0] = lower_32_bits(dma); 1238c2ecf20Sopenharmony_ci cbd.addr[1] = upper_32_bits(dma); 1248c2ecf20Sopenharmony_ci cbd.cls = BDCR_CMD_PORT_GCL; 1258c2ecf20Sopenharmony_ci cbd.status_flags = 0; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci enetc_wr(hw, ENETC_QBV_PTGCR_OFFSET, tge | ENETC_QBV_TGE); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci err = enetc_send_cmd(priv->si, &cbd); 1308c2ecf20Sopenharmony_ci if (err) 1318c2ecf20Sopenharmony_ci enetc_wr(hw, ENETC_QBV_PTGCR_OFFSET, tge & ~ENETC_QBV_TGE); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_TO_DEVICE); 1348c2ecf20Sopenharmony_ci kfree(gcl_data); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!err) 1378c2ecf20Sopenharmony_ci priv->active_offloads |= ENETC_F_QBV; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return err; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciint enetc_setup_tc_taprio(struct net_device *ndev, void *type_data) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct tc_taprio_qopt_offload *taprio = type_data; 1458c2ecf20Sopenharmony_ci struct enetc_ndev_priv *priv = netdev_priv(ndev); 1468c2ecf20Sopenharmony_ci struct enetc_hw *hw = &priv->si->hw; 1478c2ecf20Sopenharmony_ci struct enetc_bdr *tx_ring; 1488c2ecf20Sopenharmony_ci int err; 1498c2ecf20Sopenharmony_ci int i; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* TSD and Qbv are mutually exclusive in hardware */ 1528c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_tx_rings; i++) 1538c2ecf20Sopenharmony_ci if (priv->tx_ring[i]->tsd_enable) 1548c2ecf20Sopenharmony_ci return -EBUSY; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_tx_rings; i++) { 1578c2ecf20Sopenharmony_ci tx_ring = priv->tx_ring[i]; 1588c2ecf20Sopenharmony_ci tx_ring->prio = taprio->enable ? i : 0; 1598c2ecf20Sopenharmony_ci enetc_set_bdr_prio(hw, tx_ring->index, tx_ring->prio); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci err = enetc_setup_taprio(ndev, taprio); 1638c2ecf20Sopenharmony_ci if (err) { 1648c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_tx_rings; i++) { 1658c2ecf20Sopenharmony_ci tx_ring = priv->tx_ring[i]; 1668c2ecf20Sopenharmony_ci tx_ring->prio = taprio->enable ? 0 : i; 1678c2ecf20Sopenharmony_ci enetc_set_bdr_prio(hw, tx_ring->index, tx_ring->prio); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return err; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic u32 enetc_get_cbs_enable(struct enetc_hw *hw, u8 tc) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBSE; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic u8 enetc_get_cbs_bw(struct enetc_hw *hw, u8 tc) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBS_BW_MASK; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciint enetc_setup_tc_cbs(struct net_device *ndev, void *type_data) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct enetc_ndev_priv *priv = netdev_priv(ndev); 1878c2ecf20Sopenharmony_ci struct tc_cbs_qopt_offload *cbs = type_data; 1888c2ecf20Sopenharmony_ci u32 port_transmit_rate = priv->speed; 1898c2ecf20Sopenharmony_ci u8 tc_nums = netdev_get_num_tc(ndev); 1908c2ecf20Sopenharmony_ci struct enetc_hw *hw = &priv->si->hw; 1918c2ecf20Sopenharmony_ci u32 hi_credit_bit, hi_credit_reg; 1928c2ecf20Sopenharmony_ci u32 max_interference_size; 1938c2ecf20Sopenharmony_ci u32 port_frame_max_size; 1948c2ecf20Sopenharmony_ci u8 tc = cbs->queue; 1958c2ecf20Sopenharmony_ci u8 prio_top, prio_next; 1968c2ecf20Sopenharmony_ci int bw_sum = 0; 1978c2ecf20Sopenharmony_ci u8 bw; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci prio_top = tc_nums - 1; 2008c2ecf20Sopenharmony_ci prio_next = tc_nums - 2; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Support highest prio and second prio tc in cbs mode */ 2038c2ecf20Sopenharmony_ci if (tc != prio_top && tc != prio_next) 2048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (!cbs->enable) { 2078c2ecf20Sopenharmony_ci /* Make sure the other TC that are numerically 2088c2ecf20Sopenharmony_ci * lower than this TC have been disabled. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci if (tc == prio_top && 2118c2ecf20Sopenharmony_ci enetc_get_cbs_enable(hw, prio_next)) { 2128c2ecf20Sopenharmony_ci dev_err(&ndev->dev, 2138c2ecf20Sopenharmony_ci "Disable TC%d before disable TC%d\n", 2148c2ecf20Sopenharmony_ci prio_next, tc); 2158c2ecf20Sopenharmony_ci return -EINVAL; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci enetc_port_wr(hw, ENETC_PTCCBSR1(tc), 0); 2198c2ecf20Sopenharmony_ci enetc_port_wr(hw, ENETC_PTCCBSR0(tc), 0); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (cbs->idleslope - cbs->sendslope != port_transmit_rate * 1000L || 2258c2ecf20Sopenharmony_ci cbs->idleslope < 0 || cbs->sendslope > 0) 2268c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci port_frame_max_size = ndev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci bw = cbs->idleslope / (port_transmit_rate * 10UL); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Make sure the other TC that are numerically 2338c2ecf20Sopenharmony_ci * higher than this TC have been enabled. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci if (tc == prio_next) { 2368c2ecf20Sopenharmony_ci if (!enetc_get_cbs_enable(hw, prio_top)) { 2378c2ecf20Sopenharmony_ci dev_err(&ndev->dev, 2388c2ecf20Sopenharmony_ci "Enable TC%d first before enable TC%d\n", 2398c2ecf20Sopenharmony_ci prio_top, prio_next); 2408c2ecf20Sopenharmony_ci return -EINVAL; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci bw_sum += enetc_get_cbs_bw(hw, prio_top); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (bw_sum + bw >= 100) { 2468c2ecf20Sopenharmony_ci dev_err(&ndev->dev, 2478c2ecf20Sopenharmony_ci "The sum of all CBS Bandwidth can't exceed 100\n"); 2488c2ecf20Sopenharmony_ci return -EINVAL; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci enetc_port_rd(hw, ENETC_PTCMSDUR(tc)); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* For top prio TC, the max_interfrence_size is maxSizedFrame. 2548c2ecf20Sopenharmony_ci * 2558c2ecf20Sopenharmony_ci * For next prio TC, the max_interfrence_size is calculated as below: 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * max_interference_size = M0 + Ma + Ra * M0 / (R0 - Ra) 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * - RA: idleSlope for AVB Class A 2608c2ecf20Sopenharmony_ci * - R0: port transmit rate 2618c2ecf20Sopenharmony_ci * - M0: maximum sized frame for the port 2628c2ecf20Sopenharmony_ci * - MA: maximum sized frame for AVB Class A 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (tc == prio_top) { 2668c2ecf20Sopenharmony_ci max_interference_size = port_frame_max_size * 8; 2678c2ecf20Sopenharmony_ci } else { 2688c2ecf20Sopenharmony_ci u32 m0, ma, r0, ra; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci m0 = port_frame_max_size * 8; 2718c2ecf20Sopenharmony_ci ma = enetc_port_rd(hw, ENETC_PTCMSDUR(prio_top)) * 8; 2728c2ecf20Sopenharmony_ci ra = enetc_get_cbs_bw(hw, prio_top) * 2738c2ecf20Sopenharmony_ci port_transmit_rate * 10000ULL; 2748c2ecf20Sopenharmony_ci r0 = port_transmit_rate * 1000000ULL; 2758c2ecf20Sopenharmony_ci max_interference_size = m0 + ma + 2768c2ecf20Sopenharmony_ci (u32)div_u64((u64)ra * m0, r0 - ra); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* hiCredit bits calculate by: 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * maxSizedFrame * (idleSlope/portTxRate) 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci hi_credit_bit = max_interference_size * bw / 100; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* hiCredit bits to hiCredit register need to calculated as: 2868c2ecf20Sopenharmony_ci * 2878c2ecf20Sopenharmony_ci * (enetClockFrequency / portTransmitRate) * 100 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci hi_credit_reg = (u32)div_u64((ENETC_CLK * 100ULL) * hi_credit_bit, 2908c2ecf20Sopenharmony_ci port_transmit_rate * 1000000ULL); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci enetc_port_wr(hw, ENETC_PTCCBSR1(tc), hi_credit_reg); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Set bw register and enable this traffic class */ 2958c2ecf20Sopenharmony_ci enetc_port_wr(hw, ENETC_PTCCBSR0(tc), bw | ENETC_CBSE); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ciint enetc_setup_tc_txtime(struct net_device *ndev, void *type_data) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct enetc_ndev_priv *priv = netdev_priv(ndev); 3038c2ecf20Sopenharmony_ci struct tc_etf_qopt_offload *qopt = type_data; 3048c2ecf20Sopenharmony_ci u8 tc_nums = netdev_get_num_tc(ndev); 3058c2ecf20Sopenharmony_ci struct enetc_hw *hw = &priv->si->hw; 3068c2ecf20Sopenharmony_ci int tc; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!tc_nums) 3098c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci tc = qopt->queue; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (tc < 0 || tc >= priv->num_tx_rings) 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Do not support TXSTART and TX CSUM offload simutaniously */ 3178c2ecf20Sopenharmony_ci if (ndev->features & NETIF_F_CSUM_MASK) 3188c2ecf20Sopenharmony_ci return -EBUSY; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* TSD and Qbv are mutually exclusive in hardware */ 3218c2ecf20Sopenharmony_ci if (enetc_rd(hw, ENETC_QBV_PTGCR_OFFSET) & ENETC_QBV_TGE) 3228c2ecf20Sopenharmony_ci return -EBUSY; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci priv->tx_ring[tc]->tsd_enable = qopt->enable; 3258c2ecf20Sopenharmony_ci enetc_port_wr(hw, ENETC_PTCTSDR(tc), qopt->enable ? ENETC_TSDE : 0); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cienum streamid_type { 3318c2ecf20Sopenharmony_ci STREAMID_TYPE_RESERVED = 0, 3328c2ecf20Sopenharmony_ci STREAMID_TYPE_NULL, 3338c2ecf20Sopenharmony_ci STREAMID_TYPE_SMAC, 3348c2ecf20Sopenharmony_ci}; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cienum streamid_vlan_tagged { 3378c2ecf20Sopenharmony_ci STREAMID_VLAN_RESERVED = 0, 3388c2ecf20Sopenharmony_ci STREAMID_VLAN_TAGGED, 3398c2ecf20Sopenharmony_ci STREAMID_VLAN_UNTAGGED, 3408c2ecf20Sopenharmony_ci STREAMID_VLAN_ALL, 3418c2ecf20Sopenharmony_ci}; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci#define ENETC_PSFP_WILDCARD -1 3448c2ecf20Sopenharmony_ci#define HANDLE_OFFSET 100 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cienum forward_type { 3478c2ecf20Sopenharmony_ci FILTER_ACTION_TYPE_PSFP = BIT(0), 3488c2ecf20Sopenharmony_ci FILTER_ACTION_TYPE_ACL = BIT(1), 3498c2ecf20Sopenharmony_ci FILTER_ACTION_TYPE_BOTH = GENMASK(1, 0), 3508c2ecf20Sopenharmony_ci}; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* This is for limit output type for input actions */ 3538c2ecf20Sopenharmony_cistruct actions_fwd { 3548c2ecf20Sopenharmony_ci u64 actions; 3558c2ecf20Sopenharmony_ci u64 keys; /* include the must needed keys */ 3568c2ecf20Sopenharmony_ci enum forward_type output; 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistruct psfp_streamfilter_counters { 3608c2ecf20Sopenharmony_ci u64 matching_frames_count; 3618c2ecf20Sopenharmony_ci u64 passing_frames_count; 3628c2ecf20Sopenharmony_ci u64 not_passing_frames_count; 3638c2ecf20Sopenharmony_ci u64 passing_sdu_count; 3648c2ecf20Sopenharmony_ci u64 not_passing_sdu_count; 3658c2ecf20Sopenharmony_ci u64 red_frames_count; 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistruct enetc_streamid { 3698c2ecf20Sopenharmony_ci u32 index; 3708c2ecf20Sopenharmony_ci union { 3718c2ecf20Sopenharmony_ci u8 src_mac[6]; 3728c2ecf20Sopenharmony_ci u8 dst_mac[6]; 3738c2ecf20Sopenharmony_ci }; 3748c2ecf20Sopenharmony_ci u8 filtertype; 3758c2ecf20Sopenharmony_ci u16 vid; 3768c2ecf20Sopenharmony_ci u8 tagged; 3778c2ecf20Sopenharmony_ci s32 handle; 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistruct enetc_psfp_filter { 3818c2ecf20Sopenharmony_ci u32 index; 3828c2ecf20Sopenharmony_ci s32 handle; 3838c2ecf20Sopenharmony_ci s8 prio; 3848c2ecf20Sopenharmony_ci u32 maxsdu; 3858c2ecf20Sopenharmony_ci u32 gate_id; 3868c2ecf20Sopenharmony_ci s32 meter_id; 3878c2ecf20Sopenharmony_ci refcount_t refcount; 3888c2ecf20Sopenharmony_ci struct hlist_node node; 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistruct enetc_psfp_gate { 3928c2ecf20Sopenharmony_ci u32 index; 3938c2ecf20Sopenharmony_ci s8 init_ipv; 3948c2ecf20Sopenharmony_ci u64 basetime; 3958c2ecf20Sopenharmony_ci u64 cycletime; 3968c2ecf20Sopenharmony_ci u64 cycletimext; 3978c2ecf20Sopenharmony_ci u32 num_entries; 3988c2ecf20Sopenharmony_ci refcount_t refcount; 3998c2ecf20Sopenharmony_ci struct hlist_node node; 4008c2ecf20Sopenharmony_ci struct action_gate_entry entries[]; 4018c2ecf20Sopenharmony_ci}; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/* Only enable the green color frame now 4048c2ecf20Sopenharmony_ci * Will add eir and ebs color blind, couple flag etc when 4058c2ecf20Sopenharmony_ci * policing action add more offloading parameters 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistruct enetc_psfp_meter { 4088c2ecf20Sopenharmony_ci u32 index; 4098c2ecf20Sopenharmony_ci u32 cir; 4108c2ecf20Sopenharmony_ci u32 cbs; 4118c2ecf20Sopenharmony_ci refcount_t refcount; 4128c2ecf20Sopenharmony_ci struct hlist_node node; 4138c2ecf20Sopenharmony_ci}; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci#define ENETC_PSFP_FLAGS_FMI BIT(0) 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistruct enetc_stream_filter { 4188c2ecf20Sopenharmony_ci struct enetc_streamid sid; 4198c2ecf20Sopenharmony_ci u32 sfi_index; 4208c2ecf20Sopenharmony_ci u32 sgi_index; 4218c2ecf20Sopenharmony_ci u32 flags; 4228c2ecf20Sopenharmony_ci u32 fmi_index; 4238c2ecf20Sopenharmony_ci struct flow_stats stats; 4248c2ecf20Sopenharmony_ci struct hlist_node node; 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistruct enetc_psfp { 4288c2ecf20Sopenharmony_ci unsigned long dev_bitmap; 4298c2ecf20Sopenharmony_ci unsigned long *psfp_sfi_bitmap; 4308c2ecf20Sopenharmony_ci struct hlist_head stream_list; 4318c2ecf20Sopenharmony_ci struct hlist_head psfp_filter_list; 4328c2ecf20Sopenharmony_ci struct hlist_head psfp_gate_list; 4338c2ecf20Sopenharmony_ci struct hlist_head psfp_meter_list; 4348c2ecf20Sopenharmony_ci spinlock_t psfp_lock; /* spinlock for the struct enetc_psfp r/w */ 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic struct actions_fwd enetc_act_fwd[] = { 4388c2ecf20Sopenharmony_ci { 4398c2ecf20Sopenharmony_ci BIT(FLOW_ACTION_GATE), 4408c2ecf20Sopenharmony_ci BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS), 4418c2ecf20Sopenharmony_ci FILTER_ACTION_TYPE_PSFP 4428c2ecf20Sopenharmony_ci }, 4438c2ecf20Sopenharmony_ci { 4448c2ecf20Sopenharmony_ci BIT(FLOW_ACTION_POLICE) | 4458c2ecf20Sopenharmony_ci BIT(FLOW_ACTION_GATE), 4468c2ecf20Sopenharmony_ci BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS), 4478c2ecf20Sopenharmony_ci FILTER_ACTION_TYPE_PSFP 4488c2ecf20Sopenharmony_ci }, 4498c2ecf20Sopenharmony_ci /* example for ACL actions */ 4508c2ecf20Sopenharmony_ci { 4518c2ecf20Sopenharmony_ci BIT(FLOW_ACTION_DROP), 4528c2ecf20Sopenharmony_ci 0, 4538c2ecf20Sopenharmony_ci FILTER_ACTION_TYPE_ACL 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci}; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic struct enetc_psfp epsfp = { 4588c2ecf20Sopenharmony_ci .psfp_sfi_bitmap = NULL, 4598c2ecf20Sopenharmony_ci}; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic LIST_HEAD(enetc_block_cb_list); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic inline int enetc_get_port(struct enetc_ndev_priv *priv) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci return priv->si->pdev->devfn & 0x7; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/* Stream Identity Entry Set Descriptor */ 4698c2ecf20Sopenharmony_cistatic int enetc_streamid_hw_set(struct enetc_ndev_priv *priv, 4708c2ecf20Sopenharmony_ci struct enetc_streamid *sid, 4718c2ecf20Sopenharmony_ci u8 enable) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct enetc_cbd cbd = {.cmd = 0}; 4748c2ecf20Sopenharmony_ci struct streamid_data *si_data; 4758c2ecf20Sopenharmony_ci struct streamid_conf *si_conf; 4768c2ecf20Sopenharmony_ci u16 data_size; 4778c2ecf20Sopenharmony_ci dma_addr_t dma; 4788c2ecf20Sopenharmony_ci int err; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (sid->index >= priv->psfp_cap.max_streamid) 4818c2ecf20Sopenharmony_ci return -EINVAL; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (sid->filtertype != STREAMID_TYPE_NULL && 4848c2ecf20Sopenharmony_ci sid->filtertype != STREAMID_TYPE_SMAC) 4858c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Disable operation before enable */ 4888c2ecf20Sopenharmony_ci cbd.index = cpu_to_le16((u16)sid->index); 4898c2ecf20Sopenharmony_ci cbd.cls = BDCR_CMD_STREAM_IDENTIFY; 4908c2ecf20Sopenharmony_ci cbd.status_flags = 0; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci data_size = sizeof(struct streamid_data); 4938c2ecf20Sopenharmony_ci si_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); 4948c2ecf20Sopenharmony_ci if (!si_data) 4958c2ecf20Sopenharmony_ci return -ENOMEM; 4968c2ecf20Sopenharmony_ci cbd.length = cpu_to_le16(data_size); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci dma = dma_map_single(&priv->si->pdev->dev, si_data, 4998c2ecf20Sopenharmony_ci data_size, DMA_FROM_DEVICE); 5008c2ecf20Sopenharmony_ci if (dma_mapping_error(&priv->si->pdev->dev, dma)) { 5018c2ecf20Sopenharmony_ci netdev_err(priv->si->ndev, "DMA mapping failed!\n"); 5028c2ecf20Sopenharmony_ci err = -ENOMEM; 5038c2ecf20Sopenharmony_ci goto out; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci cbd.addr[0] = lower_32_bits(dma); 5078c2ecf20Sopenharmony_ci cbd.addr[1] = upper_32_bits(dma); 5088c2ecf20Sopenharmony_ci eth_broadcast_addr(si_data->dmac); 5098c2ecf20Sopenharmony_ci si_data->vid_vidm_tg = 5108c2ecf20Sopenharmony_ci cpu_to_le16(ENETC_CBDR_SID_VID_MASK 5118c2ecf20Sopenharmony_ci + ((0x3 << 14) | ENETC_CBDR_SID_VIDM)); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci si_conf = &cbd.sid_set; 5148c2ecf20Sopenharmony_ci /* Only one port supported for one entry, set itself */ 5158c2ecf20Sopenharmony_ci si_conf->iports = 1 << enetc_get_port(priv); 5168c2ecf20Sopenharmony_ci si_conf->id_type = 1; 5178c2ecf20Sopenharmony_ci si_conf->oui[2] = 0x0; 5188c2ecf20Sopenharmony_ci si_conf->oui[1] = 0x80; 5198c2ecf20Sopenharmony_ci si_conf->oui[0] = 0xC2; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci err = enetc_send_cmd(priv->si, &cbd); 5228c2ecf20Sopenharmony_ci if (err) 5238c2ecf20Sopenharmony_ci goto out; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (!enable) 5268c2ecf20Sopenharmony_ci goto out; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* Enable the entry overwrite again incase space flushed by hardware */ 5298c2ecf20Sopenharmony_ci memset(&cbd, 0, sizeof(cbd)); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci cbd.index = cpu_to_le16((u16)sid->index); 5328c2ecf20Sopenharmony_ci cbd.cmd = 0; 5338c2ecf20Sopenharmony_ci cbd.cls = BDCR_CMD_STREAM_IDENTIFY; 5348c2ecf20Sopenharmony_ci cbd.status_flags = 0; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci si_conf->en = 0x80; 5378c2ecf20Sopenharmony_ci si_conf->stream_handle = cpu_to_le32(sid->handle); 5388c2ecf20Sopenharmony_ci si_conf->iports = 1 << enetc_get_port(priv); 5398c2ecf20Sopenharmony_ci si_conf->id_type = sid->filtertype; 5408c2ecf20Sopenharmony_ci si_conf->oui[2] = 0x0; 5418c2ecf20Sopenharmony_ci si_conf->oui[1] = 0x80; 5428c2ecf20Sopenharmony_ci si_conf->oui[0] = 0xC2; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci memset(si_data, 0, data_size); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci cbd.length = cpu_to_le16(data_size); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci cbd.addr[0] = lower_32_bits(dma); 5498c2ecf20Sopenharmony_ci cbd.addr[1] = upper_32_bits(dma); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* VIDM default to be 1. 5528c2ecf20Sopenharmony_ci * VID Match. If set (b1) then the VID must match, otherwise 5538c2ecf20Sopenharmony_ci * any VID is considered a match. VIDM setting is only used 5548c2ecf20Sopenharmony_ci * when TG is set to b01. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci if (si_conf->id_type == STREAMID_TYPE_NULL) { 5578c2ecf20Sopenharmony_ci ether_addr_copy(si_data->dmac, sid->dst_mac); 5588c2ecf20Sopenharmony_ci si_data->vid_vidm_tg = 5598c2ecf20Sopenharmony_ci cpu_to_le16((sid->vid & ENETC_CBDR_SID_VID_MASK) + 5608c2ecf20Sopenharmony_ci ((((u16)(sid->tagged) & 0x3) << 14) 5618c2ecf20Sopenharmony_ci | ENETC_CBDR_SID_VIDM)); 5628c2ecf20Sopenharmony_ci } else if (si_conf->id_type == STREAMID_TYPE_SMAC) { 5638c2ecf20Sopenharmony_ci ether_addr_copy(si_data->smac, sid->src_mac); 5648c2ecf20Sopenharmony_ci si_data->vid_vidm_tg = 5658c2ecf20Sopenharmony_ci cpu_to_le16((sid->vid & ENETC_CBDR_SID_VID_MASK) + 5668c2ecf20Sopenharmony_ci ((((u16)(sid->tagged) & 0x3) << 14) 5678c2ecf20Sopenharmony_ci | ENETC_CBDR_SID_VIDM)); 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci err = enetc_send_cmd(priv->si, &cbd); 5718c2ecf20Sopenharmony_ciout: 5728c2ecf20Sopenharmony_ci if (!dma_mapping_error(&priv->si->pdev->dev, dma)) 5738c2ecf20Sopenharmony_ci dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_FROM_DEVICE); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci kfree(si_data); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci return err; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/* Stream Filter Instance Set Descriptor */ 5818c2ecf20Sopenharmony_cistatic int enetc_streamfilter_hw_set(struct enetc_ndev_priv *priv, 5828c2ecf20Sopenharmony_ci struct enetc_psfp_filter *sfi, 5838c2ecf20Sopenharmony_ci u8 enable) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct enetc_cbd cbd = {.cmd = 0}; 5868c2ecf20Sopenharmony_ci struct sfi_conf *sfi_config; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci cbd.index = cpu_to_le16(sfi->index); 5898c2ecf20Sopenharmony_ci cbd.cls = BDCR_CMD_STREAM_FILTER; 5908c2ecf20Sopenharmony_ci cbd.status_flags = 0x80; 5918c2ecf20Sopenharmony_ci cbd.length = cpu_to_le16(1); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci sfi_config = &cbd.sfi_conf; 5948c2ecf20Sopenharmony_ci if (!enable) 5958c2ecf20Sopenharmony_ci goto exit; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci sfi_config->en = 0x80; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (sfi->handle >= 0) { 6008c2ecf20Sopenharmony_ci sfi_config->stream_handle = 6018c2ecf20Sopenharmony_ci cpu_to_le32(sfi->handle); 6028c2ecf20Sopenharmony_ci sfi_config->sthm |= 0x80; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci sfi_config->sg_inst_table_index = cpu_to_le16(sfi->gate_id); 6068c2ecf20Sopenharmony_ci sfi_config->input_ports = 1 << enetc_get_port(priv); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* The priority value which may be matched against the 6098c2ecf20Sopenharmony_ci * frame’s priority value to determine a match for this entry. 6108c2ecf20Sopenharmony_ci */ 6118c2ecf20Sopenharmony_ci if (sfi->prio >= 0) 6128c2ecf20Sopenharmony_ci sfi_config->multi |= (sfi->prio & 0x7) | 0x8; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* Filter Type. Identifies the contents of the MSDU/FM_INST_INDEX 6158c2ecf20Sopenharmony_ci * field as being either an MSDU value or an index into the Flow 6168c2ecf20Sopenharmony_ci * Meter Instance table. 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci if (sfi->maxsdu) { 6198c2ecf20Sopenharmony_ci sfi_config->msdu = 6208c2ecf20Sopenharmony_ci cpu_to_le16(sfi->maxsdu); 6218c2ecf20Sopenharmony_ci sfi_config->multi |= 0x40; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (sfi->meter_id >= 0) { 6258c2ecf20Sopenharmony_ci sfi_config->fm_inst_table_index = cpu_to_le16(sfi->meter_id); 6268c2ecf20Sopenharmony_ci sfi_config->multi |= 0x80; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ciexit: 6308c2ecf20Sopenharmony_ci return enetc_send_cmd(priv->si, &cbd); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int enetc_streamcounter_hw_get(struct enetc_ndev_priv *priv, 6348c2ecf20Sopenharmony_ci u32 index, 6358c2ecf20Sopenharmony_ci struct psfp_streamfilter_counters *cnt) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci struct enetc_cbd cbd = { .cmd = 2 }; 6388c2ecf20Sopenharmony_ci struct sfi_counter_data *data_buf; 6398c2ecf20Sopenharmony_ci dma_addr_t dma; 6408c2ecf20Sopenharmony_ci u16 data_size; 6418c2ecf20Sopenharmony_ci int err; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci cbd.index = cpu_to_le16((u16)index); 6448c2ecf20Sopenharmony_ci cbd.cmd = 2; 6458c2ecf20Sopenharmony_ci cbd.cls = BDCR_CMD_STREAM_FILTER; 6468c2ecf20Sopenharmony_ci cbd.status_flags = 0; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci data_size = sizeof(struct sfi_counter_data); 6498c2ecf20Sopenharmony_ci data_buf = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); 6508c2ecf20Sopenharmony_ci if (!data_buf) 6518c2ecf20Sopenharmony_ci return -ENOMEM; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci dma = dma_map_single(&priv->si->pdev->dev, data_buf, 6548c2ecf20Sopenharmony_ci data_size, DMA_FROM_DEVICE); 6558c2ecf20Sopenharmony_ci if (dma_mapping_error(&priv->si->pdev->dev, dma)) { 6568c2ecf20Sopenharmony_ci netdev_err(priv->si->ndev, "DMA mapping failed!\n"); 6578c2ecf20Sopenharmony_ci err = -ENOMEM; 6588c2ecf20Sopenharmony_ci goto exit; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci cbd.addr[0] = lower_32_bits(dma); 6618c2ecf20Sopenharmony_ci cbd.addr[1] = upper_32_bits(dma); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci cbd.length = cpu_to_le16(data_size); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci err = enetc_send_cmd(priv->si, &cbd); 6668c2ecf20Sopenharmony_ci if (err) 6678c2ecf20Sopenharmony_ci goto exit; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci cnt->matching_frames_count = 6708c2ecf20Sopenharmony_ci ((u64)le32_to_cpu(data_buf->matchh) << 32) 6718c2ecf20Sopenharmony_ci + data_buf->matchl; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci cnt->not_passing_sdu_count = 6748c2ecf20Sopenharmony_ci ((u64)le32_to_cpu(data_buf->msdu_droph) << 32) 6758c2ecf20Sopenharmony_ci + data_buf->msdu_dropl; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci cnt->passing_sdu_count = cnt->matching_frames_count 6788c2ecf20Sopenharmony_ci - cnt->not_passing_sdu_count; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci cnt->not_passing_frames_count = 6818c2ecf20Sopenharmony_ci ((u64)le32_to_cpu(data_buf->stream_gate_droph) << 32) 6828c2ecf20Sopenharmony_ci + le32_to_cpu(data_buf->stream_gate_dropl); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci cnt->passing_frames_count = cnt->matching_frames_count 6858c2ecf20Sopenharmony_ci - cnt->not_passing_sdu_count 6868c2ecf20Sopenharmony_ci - cnt->not_passing_frames_count; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci cnt->red_frames_count = 6898c2ecf20Sopenharmony_ci ((u64)le32_to_cpu(data_buf->flow_meter_droph) << 32) 6908c2ecf20Sopenharmony_ci + le32_to_cpu(data_buf->flow_meter_dropl); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ciexit: 6938c2ecf20Sopenharmony_ci kfree(data_buf); 6948c2ecf20Sopenharmony_ci return err; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic u64 get_ptp_now(struct enetc_hw *hw) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci u64 now_lo, now_hi, now; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci now_lo = enetc_rd(hw, ENETC_SICTR0); 7028c2ecf20Sopenharmony_ci now_hi = enetc_rd(hw, ENETC_SICTR1); 7038c2ecf20Sopenharmony_ci now = now_lo | now_hi << 32; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci return now; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic int get_start_ns(u64 now, u64 cycle, u64 *start) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci u64 n; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (!cycle) 7138c2ecf20Sopenharmony_ci return -EFAULT; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci n = div64_u64(now, cycle); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci *start = (n + 1) * cycle; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci return 0; 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci/* Stream Gate Instance Set Descriptor */ 7238c2ecf20Sopenharmony_cistatic int enetc_streamgate_hw_set(struct enetc_ndev_priv *priv, 7248c2ecf20Sopenharmony_ci struct enetc_psfp_gate *sgi, 7258c2ecf20Sopenharmony_ci u8 enable) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci struct enetc_cbd cbd = { .cmd = 0 }; 7288c2ecf20Sopenharmony_ci struct sgi_table *sgi_config; 7298c2ecf20Sopenharmony_ci struct sgcl_conf *sgcl_config; 7308c2ecf20Sopenharmony_ci struct sgcl_data *sgcl_data; 7318c2ecf20Sopenharmony_ci struct sgce *sgce; 7328c2ecf20Sopenharmony_ci dma_addr_t dma; 7338c2ecf20Sopenharmony_ci u16 data_size; 7348c2ecf20Sopenharmony_ci int err, i; 7358c2ecf20Sopenharmony_ci u64 now; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci cbd.index = cpu_to_le16(sgi->index); 7388c2ecf20Sopenharmony_ci cbd.cmd = 0; 7398c2ecf20Sopenharmony_ci cbd.cls = BDCR_CMD_STREAM_GCL; 7408c2ecf20Sopenharmony_ci cbd.status_flags = 0x80; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* disable */ 7438c2ecf20Sopenharmony_ci if (!enable) 7448c2ecf20Sopenharmony_ci return enetc_send_cmd(priv->si, &cbd); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (!sgi->num_entries) 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (sgi->num_entries > priv->psfp_cap.max_psfp_gatelist || 7508c2ecf20Sopenharmony_ci !sgi->cycletime) 7518c2ecf20Sopenharmony_ci return -EINVAL; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* enable */ 7548c2ecf20Sopenharmony_ci sgi_config = &cbd.sgi_table; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Keep open before gate list start */ 7578c2ecf20Sopenharmony_ci sgi_config->ocgtst = 0x80; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci sgi_config->oipv = (sgi->init_ipv < 0) ? 7608c2ecf20Sopenharmony_ci 0x0 : ((sgi->init_ipv & 0x7) | 0x8); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci sgi_config->en = 0x80; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Basic config */ 7658c2ecf20Sopenharmony_ci err = enetc_send_cmd(priv->si, &cbd); 7668c2ecf20Sopenharmony_ci if (err) 7678c2ecf20Sopenharmony_ci return -EINVAL; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci memset(&cbd, 0, sizeof(cbd)); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci cbd.index = cpu_to_le16(sgi->index); 7728c2ecf20Sopenharmony_ci cbd.cmd = 1; 7738c2ecf20Sopenharmony_ci cbd.cls = BDCR_CMD_STREAM_GCL; 7748c2ecf20Sopenharmony_ci cbd.status_flags = 0; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci sgcl_config = &cbd.sgcl_conf; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci sgcl_config->acl_len = (sgi->num_entries - 1) & 0x3; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci data_size = struct_size(sgcl_data, sgcl, sgi->num_entries); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci sgcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); 7838c2ecf20Sopenharmony_ci if (!sgcl_data) 7848c2ecf20Sopenharmony_ci return -ENOMEM; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci cbd.length = cpu_to_le16(data_size); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci dma = dma_map_single(&priv->si->pdev->dev, 7898c2ecf20Sopenharmony_ci sgcl_data, data_size, 7908c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 7918c2ecf20Sopenharmony_ci if (dma_mapping_error(&priv->si->pdev->dev, dma)) { 7928c2ecf20Sopenharmony_ci netdev_err(priv->si->ndev, "DMA mapping failed!\n"); 7938c2ecf20Sopenharmony_ci kfree(sgcl_data); 7948c2ecf20Sopenharmony_ci return -ENOMEM; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci cbd.addr[0] = lower_32_bits(dma); 7988c2ecf20Sopenharmony_ci cbd.addr[1] = upper_32_bits(dma); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci sgce = &sgcl_data->sgcl[0]; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci sgcl_config->agtst = 0x80; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci sgcl_data->ct = cpu_to_le32(sgi->cycletime); 8058c2ecf20Sopenharmony_ci sgcl_data->cte = cpu_to_le32(sgi->cycletimext); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (sgi->init_ipv >= 0) 8088c2ecf20Sopenharmony_ci sgcl_config->aipv = (sgi->init_ipv & 0x7) | 0x8; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci for (i = 0; i < sgi->num_entries; i++) { 8118c2ecf20Sopenharmony_ci struct action_gate_entry *from = &sgi->entries[i]; 8128c2ecf20Sopenharmony_ci struct sgce *to = &sgce[i]; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (from->gate_state) 8158c2ecf20Sopenharmony_ci to->multi |= 0x10; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (from->ipv >= 0) 8188c2ecf20Sopenharmony_ci to->multi |= ((from->ipv & 0x7) << 5) | 0x08; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (from->maxoctets >= 0) { 8218c2ecf20Sopenharmony_ci to->multi |= 0x01; 8228c2ecf20Sopenharmony_ci to->msdu[0] = from->maxoctets & 0xFF; 8238c2ecf20Sopenharmony_ci to->msdu[1] = (from->maxoctets >> 8) & 0xFF; 8248c2ecf20Sopenharmony_ci to->msdu[2] = (from->maxoctets >> 16) & 0xFF; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci to->interval = cpu_to_le32(from->interval); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* If basetime is less than now, calculate start time */ 8318c2ecf20Sopenharmony_ci now = get_ptp_now(&priv->si->hw); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (sgi->basetime < now) { 8348c2ecf20Sopenharmony_ci u64 start; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci err = get_start_ns(now, sgi->cycletime, &start); 8378c2ecf20Sopenharmony_ci if (err) 8388c2ecf20Sopenharmony_ci goto exit; 8398c2ecf20Sopenharmony_ci sgcl_data->btl = cpu_to_le32(lower_32_bits(start)); 8408c2ecf20Sopenharmony_ci sgcl_data->bth = cpu_to_le32(upper_32_bits(start)); 8418c2ecf20Sopenharmony_ci } else { 8428c2ecf20Sopenharmony_ci u32 hi, lo; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci hi = upper_32_bits(sgi->basetime); 8458c2ecf20Sopenharmony_ci lo = lower_32_bits(sgi->basetime); 8468c2ecf20Sopenharmony_ci sgcl_data->bth = cpu_to_le32(hi); 8478c2ecf20Sopenharmony_ci sgcl_data->btl = cpu_to_le32(lo); 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci err = enetc_send_cmd(priv->si, &cbd); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ciexit: 8538c2ecf20Sopenharmony_ci kfree(sgcl_data); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return err; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic int enetc_flowmeter_hw_set(struct enetc_ndev_priv *priv, 8598c2ecf20Sopenharmony_ci struct enetc_psfp_meter *fmi, 8608c2ecf20Sopenharmony_ci u8 enable) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct enetc_cbd cbd = { .cmd = 0 }; 8638c2ecf20Sopenharmony_ci struct fmi_conf *fmi_config; 8648c2ecf20Sopenharmony_ci u64 temp = 0; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci cbd.index = cpu_to_le16((u16)fmi->index); 8678c2ecf20Sopenharmony_ci cbd.cls = BDCR_CMD_FLOW_METER; 8688c2ecf20Sopenharmony_ci cbd.status_flags = 0x80; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (!enable) 8718c2ecf20Sopenharmony_ci return enetc_send_cmd(priv->si, &cbd); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci fmi_config = &cbd.fmi_conf; 8748c2ecf20Sopenharmony_ci fmi_config->en = 0x80; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (fmi->cir) { 8778c2ecf20Sopenharmony_ci temp = (u64)8000 * fmi->cir; 8788c2ecf20Sopenharmony_ci temp = div_u64(temp, 3725); 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci fmi_config->cir = cpu_to_le32((u32)temp); 8828c2ecf20Sopenharmony_ci fmi_config->cbs = cpu_to_le32(fmi->cbs); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* Default for eir ebs disable */ 8858c2ecf20Sopenharmony_ci fmi_config->eir = 0; 8868c2ecf20Sopenharmony_ci fmi_config->ebs = 0; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci /* Default: 8898c2ecf20Sopenharmony_ci * mark red disable 8908c2ecf20Sopenharmony_ci * drop on yellow disable 8918c2ecf20Sopenharmony_ci * color mode disable 8928c2ecf20Sopenharmony_ci * couple flag disable 8938c2ecf20Sopenharmony_ci */ 8948c2ecf20Sopenharmony_ci fmi_config->conf = 0; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci return enetc_send_cmd(priv->si, &cbd); 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic struct enetc_stream_filter *enetc_get_stream_by_index(u32 index) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct enetc_stream_filter *f; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci hlist_for_each_entry(f, &epsfp.stream_list, node) 9048c2ecf20Sopenharmony_ci if (f->sid.index == index) 9058c2ecf20Sopenharmony_ci return f; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci return NULL; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic struct enetc_psfp_gate *enetc_get_gate_by_index(u32 index) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci struct enetc_psfp_gate *g; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci hlist_for_each_entry(g, &epsfp.psfp_gate_list, node) 9158c2ecf20Sopenharmony_ci if (g->index == index) 9168c2ecf20Sopenharmony_ci return g; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci return NULL; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic struct enetc_psfp_filter *enetc_get_filter_by_index(u32 index) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct enetc_psfp_filter *s; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci hlist_for_each_entry(s, &epsfp.psfp_filter_list, node) 9268c2ecf20Sopenharmony_ci if (s->index == index) 9278c2ecf20Sopenharmony_ci return s; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci return NULL; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic struct enetc_psfp_meter *enetc_get_meter_by_index(u32 index) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct enetc_psfp_meter *m; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci hlist_for_each_entry(m, &epsfp.psfp_meter_list, node) 9378c2ecf20Sopenharmony_ci if (m->index == index) 9388c2ecf20Sopenharmony_ci return m; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci return NULL; 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic struct enetc_psfp_filter 9448c2ecf20Sopenharmony_ci *enetc_psfp_check_sfi(struct enetc_psfp_filter *sfi) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci struct enetc_psfp_filter *s; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci hlist_for_each_entry(s, &epsfp.psfp_filter_list, node) 9498c2ecf20Sopenharmony_ci if (s->gate_id == sfi->gate_id && 9508c2ecf20Sopenharmony_ci s->prio == sfi->prio && 9518c2ecf20Sopenharmony_ci s->maxsdu == sfi->maxsdu && 9528c2ecf20Sopenharmony_ci s->meter_id == sfi->meter_id) 9538c2ecf20Sopenharmony_ci return s; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci return NULL; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic int enetc_get_free_index(struct enetc_ndev_priv *priv) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci u32 max_size = priv->psfp_cap.max_psfp_filter; 9618c2ecf20Sopenharmony_ci unsigned long index; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci index = find_first_zero_bit(epsfp.psfp_sfi_bitmap, max_size); 9648c2ecf20Sopenharmony_ci if (index == max_size) 9658c2ecf20Sopenharmony_ci return -1; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return index; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic void stream_filter_unref(struct enetc_ndev_priv *priv, u32 index) 9718c2ecf20Sopenharmony_ci{ 9728c2ecf20Sopenharmony_ci struct enetc_psfp_filter *sfi; 9738c2ecf20Sopenharmony_ci u8 z; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci sfi = enetc_get_filter_by_index(index); 9768c2ecf20Sopenharmony_ci WARN_ON(!sfi); 9778c2ecf20Sopenharmony_ci z = refcount_dec_and_test(&sfi->refcount); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci if (z) { 9808c2ecf20Sopenharmony_ci enetc_streamfilter_hw_set(priv, sfi, false); 9818c2ecf20Sopenharmony_ci hlist_del(&sfi->node); 9828c2ecf20Sopenharmony_ci kfree(sfi); 9838c2ecf20Sopenharmony_ci clear_bit(index, epsfp.psfp_sfi_bitmap); 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic void stream_gate_unref(struct enetc_ndev_priv *priv, u32 index) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci struct enetc_psfp_gate *sgi; 9908c2ecf20Sopenharmony_ci u8 z; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci sgi = enetc_get_gate_by_index(index); 9938c2ecf20Sopenharmony_ci WARN_ON(!sgi); 9948c2ecf20Sopenharmony_ci z = refcount_dec_and_test(&sgi->refcount); 9958c2ecf20Sopenharmony_ci if (z) { 9968c2ecf20Sopenharmony_ci enetc_streamgate_hw_set(priv, sgi, false); 9978c2ecf20Sopenharmony_ci hlist_del(&sgi->node); 9988c2ecf20Sopenharmony_ci kfree(sgi); 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic void flow_meter_unref(struct enetc_ndev_priv *priv, u32 index) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci struct enetc_psfp_meter *fmi; 10058c2ecf20Sopenharmony_ci u8 z; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci fmi = enetc_get_meter_by_index(index); 10088c2ecf20Sopenharmony_ci WARN_ON(!fmi); 10098c2ecf20Sopenharmony_ci z = refcount_dec_and_test(&fmi->refcount); 10108c2ecf20Sopenharmony_ci if (z) { 10118c2ecf20Sopenharmony_ci enetc_flowmeter_hw_set(priv, fmi, false); 10128c2ecf20Sopenharmony_ci hlist_del(&fmi->node); 10138c2ecf20Sopenharmony_ci kfree(fmi); 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic void remove_one_chain(struct enetc_ndev_priv *priv, 10188c2ecf20Sopenharmony_ci struct enetc_stream_filter *filter) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci if (filter->flags & ENETC_PSFP_FLAGS_FMI) 10218c2ecf20Sopenharmony_ci flow_meter_unref(priv, filter->fmi_index); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci stream_gate_unref(priv, filter->sgi_index); 10248c2ecf20Sopenharmony_ci stream_filter_unref(priv, filter->sfi_index); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci hlist_del(&filter->node); 10278c2ecf20Sopenharmony_ci kfree(filter); 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic int enetc_psfp_hw_set(struct enetc_ndev_priv *priv, 10318c2ecf20Sopenharmony_ci struct enetc_streamid *sid, 10328c2ecf20Sopenharmony_ci struct enetc_psfp_filter *sfi, 10338c2ecf20Sopenharmony_ci struct enetc_psfp_gate *sgi, 10348c2ecf20Sopenharmony_ci struct enetc_psfp_meter *fmi) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci int err; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci err = enetc_streamid_hw_set(priv, sid, true); 10398c2ecf20Sopenharmony_ci if (err) 10408c2ecf20Sopenharmony_ci return err; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (sfi) { 10438c2ecf20Sopenharmony_ci err = enetc_streamfilter_hw_set(priv, sfi, true); 10448c2ecf20Sopenharmony_ci if (err) 10458c2ecf20Sopenharmony_ci goto revert_sid; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci err = enetc_streamgate_hw_set(priv, sgi, true); 10498c2ecf20Sopenharmony_ci if (err) 10508c2ecf20Sopenharmony_ci goto revert_sfi; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (fmi) { 10538c2ecf20Sopenharmony_ci err = enetc_flowmeter_hw_set(priv, fmi, true); 10548c2ecf20Sopenharmony_ci if (err) 10558c2ecf20Sopenharmony_ci goto revert_sgi; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci return 0; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cirevert_sgi: 10618c2ecf20Sopenharmony_ci enetc_streamgate_hw_set(priv, sgi, false); 10628c2ecf20Sopenharmony_cirevert_sfi: 10638c2ecf20Sopenharmony_ci if (sfi) 10648c2ecf20Sopenharmony_ci enetc_streamfilter_hw_set(priv, sfi, false); 10658c2ecf20Sopenharmony_cirevert_sid: 10668c2ecf20Sopenharmony_ci enetc_streamid_hw_set(priv, sid, false); 10678c2ecf20Sopenharmony_ci return err; 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cistatic struct actions_fwd *enetc_check_flow_actions(u64 acts, 10718c2ecf20Sopenharmony_ci unsigned int inputkeys) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci int i; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(enetc_act_fwd); i++) 10768c2ecf20Sopenharmony_ci if (acts == enetc_act_fwd[i].actions && 10778c2ecf20Sopenharmony_ci inputkeys & enetc_act_fwd[i].keys) 10788c2ecf20Sopenharmony_ci return &enetc_act_fwd[i]; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci return NULL; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, 10848c2ecf20Sopenharmony_ci struct flow_cls_offload *f) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci struct flow_action_entry *entryg = NULL, *entryp = NULL; 10878c2ecf20Sopenharmony_ci struct flow_rule *rule = flow_cls_offload_flow_rule(f); 10888c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack = f->common.extack; 10898c2ecf20Sopenharmony_ci struct enetc_stream_filter *filter, *old_filter; 10908c2ecf20Sopenharmony_ci struct enetc_psfp_meter *fmi = NULL, *old_fmi; 10918c2ecf20Sopenharmony_ci struct enetc_psfp_filter *sfi, *old_sfi; 10928c2ecf20Sopenharmony_ci struct enetc_psfp_gate *sgi, *old_sgi; 10938c2ecf20Sopenharmony_ci struct flow_action_entry *entry; 10948c2ecf20Sopenharmony_ci struct action_gate_entry *e; 10958c2ecf20Sopenharmony_ci u8 sfi_overwrite = 0; 10968c2ecf20Sopenharmony_ci int entries_size; 10978c2ecf20Sopenharmony_ci int i, err; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (f->common.chain_index >= priv->psfp_cap.max_streamid) { 11008c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No Stream identify resource!"); 11018c2ecf20Sopenharmony_ci return -ENOSPC; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci flow_action_for_each(i, entry, &rule->action) 11058c2ecf20Sopenharmony_ci if (entry->id == FLOW_ACTION_GATE) 11068c2ecf20Sopenharmony_ci entryg = entry; 11078c2ecf20Sopenharmony_ci else if (entry->id == FLOW_ACTION_POLICE) 11088c2ecf20Sopenharmony_ci entryp = entry; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci /* Not support without gate action */ 11118c2ecf20Sopenharmony_ci if (!entryg) 11128c2ecf20Sopenharmony_ci return -EINVAL; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci filter = kzalloc(sizeof(*filter), GFP_KERNEL); 11158c2ecf20Sopenharmony_ci if (!filter) 11168c2ecf20Sopenharmony_ci return -ENOMEM; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci filter->sid.index = f->common.chain_index; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 11218c2ecf20Sopenharmony_ci struct flow_match_eth_addrs match; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci flow_rule_match_eth_addrs(rule, &match); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(match.mask->dst) && 11268c2ecf20Sopenharmony_ci !is_zero_ether_addr(match.mask->src)) { 11278c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 11288c2ecf20Sopenharmony_ci "Cannot match on both source and destination MAC"); 11298c2ecf20Sopenharmony_ci err = -EINVAL; 11308c2ecf20Sopenharmony_ci goto free_filter; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(match.mask->dst)) { 11348c2ecf20Sopenharmony_ci if (!is_broadcast_ether_addr(match.mask->dst)) { 11358c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 11368c2ecf20Sopenharmony_ci "Masked matching on destination MAC not supported"); 11378c2ecf20Sopenharmony_ci err = -EINVAL; 11388c2ecf20Sopenharmony_ci goto free_filter; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci ether_addr_copy(filter->sid.dst_mac, match.key->dst); 11418c2ecf20Sopenharmony_ci filter->sid.filtertype = STREAMID_TYPE_NULL; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(match.mask->src)) { 11458c2ecf20Sopenharmony_ci if (!is_broadcast_ether_addr(match.mask->src)) { 11468c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 11478c2ecf20Sopenharmony_ci "Masked matching on source MAC not supported"); 11488c2ecf20Sopenharmony_ci err = -EINVAL; 11498c2ecf20Sopenharmony_ci goto free_filter; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci ether_addr_copy(filter->sid.src_mac, match.key->src); 11528c2ecf20Sopenharmony_ci filter->sid.filtertype = STREAMID_TYPE_SMAC; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci } else { 11558c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported, must include ETH_ADDRS"); 11568c2ecf20Sopenharmony_ci err = -EINVAL; 11578c2ecf20Sopenharmony_ci goto free_filter; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { 11618c2ecf20Sopenharmony_ci struct flow_match_vlan match; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci flow_rule_match_vlan(rule, &match); 11648c2ecf20Sopenharmony_ci if (match.mask->vlan_priority) { 11658c2ecf20Sopenharmony_ci if (match.mask->vlan_priority != 11668c2ecf20Sopenharmony_ci (VLAN_PRIO_MASK >> VLAN_PRIO_SHIFT)) { 11678c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for VLAN priority"); 11688c2ecf20Sopenharmony_ci err = -EINVAL; 11698c2ecf20Sopenharmony_ci goto free_filter; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if (match.mask->vlan_id) { 11748c2ecf20Sopenharmony_ci if (match.mask->vlan_id != VLAN_VID_MASK) { 11758c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for VLAN id"); 11768c2ecf20Sopenharmony_ci err = -EINVAL; 11778c2ecf20Sopenharmony_ci goto free_filter; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci filter->sid.vid = match.key->vlan_id; 11818c2ecf20Sopenharmony_ci if (!filter->sid.vid) 11828c2ecf20Sopenharmony_ci filter->sid.tagged = STREAMID_VLAN_UNTAGGED; 11838c2ecf20Sopenharmony_ci else 11848c2ecf20Sopenharmony_ci filter->sid.tagged = STREAMID_VLAN_TAGGED; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci } else { 11878c2ecf20Sopenharmony_ci filter->sid.tagged = STREAMID_VLAN_ALL; 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* parsing gate action */ 11918c2ecf20Sopenharmony_ci if (entryg->gate.index >= priv->psfp_cap.max_psfp_gate) { 11928c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No Stream Gate resource!"); 11938c2ecf20Sopenharmony_ci err = -ENOSPC; 11948c2ecf20Sopenharmony_ci goto free_filter; 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (entryg->gate.num_entries >= priv->psfp_cap.max_psfp_gatelist) { 11988c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No Stream Gate resource!"); 11998c2ecf20Sopenharmony_ci err = -ENOSPC; 12008c2ecf20Sopenharmony_ci goto free_filter; 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci entries_size = struct_size(sgi, entries, entryg->gate.num_entries); 12048c2ecf20Sopenharmony_ci sgi = kzalloc(entries_size, GFP_KERNEL); 12058c2ecf20Sopenharmony_ci if (!sgi) { 12068c2ecf20Sopenharmony_ci err = -ENOMEM; 12078c2ecf20Sopenharmony_ci goto free_filter; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci refcount_set(&sgi->refcount, 1); 12118c2ecf20Sopenharmony_ci sgi->index = entryg->gate.index; 12128c2ecf20Sopenharmony_ci sgi->init_ipv = entryg->gate.prio; 12138c2ecf20Sopenharmony_ci sgi->basetime = entryg->gate.basetime; 12148c2ecf20Sopenharmony_ci sgi->cycletime = entryg->gate.cycletime; 12158c2ecf20Sopenharmony_ci sgi->num_entries = entryg->gate.num_entries; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci e = sgi->entries; 12188c2ecf20Sopenharmony_ci for (i = 0; i < entryg->gate.num_entries; i++) { 12198c2ecf20Sopenharmony_ci e[i].gate_state = entryg->gate.entries[i].gate_state; 12208c2ecf20Sopenharmony_ci e[i].interval = entryg->gate.entries[i].interval; 12218c2ecf20Sopenharmony_ci e[i].ipv = entryg->gate.entries[i].ipv; 12228c2ecf20Sopenharmony_ci e[i].maxoctets = entryg->gate.entries[i].maxoctets; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci filter->sgi_index = sgi->index; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci sfi = kzalloc(sizeof(*sfi), GFP_KERNEL); 12288c2ecf20Sopenharmony_ci if (!sfi) { 12298c2ecf20Sopenharmony_ci err = -ENOMEM; 12308c2ecf20Sopenharmony_ci goto free_gate; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci refcount_set(&sfi->refcount, 1); 12348c2ecf20Sopenharmony_ci sfi->gate_id = sgi->index; 12358c2ecf20Sopenharmony_ci sfi->meter_id = ENETC_PSFP_WILDCARD; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci /* Flow meter and max frame size */ 12388c2ecf20Sopenharmony_ci if (entryp) { 12398c2ecf20Sopenharmony_ci if (entryp->police.burst) { 12408c2ecf20Sopenharmony_ci fmi = kzalloc(sizeof(*fmi), GFP_KERNEL); 12418c2ecf20Sopenharmony_ci if (!fmi) { 12428c2ecf20Sopenharmony_ci err = -ENOMEM; 12438c2ecf20Sopenharmony_ci goto free_sfi; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci refcount_set(&fmi->refcount, 1); 12468c2ecf20Sopenharmony_ci fmi->cir = entryp->police.rate_bytes_ps; 12478c2ecf20Sopenharmony_ci fmi->cbs = entryp->police.burst; 12488c2ecf20Sopenharmony_ci fmi->index = entryp->police.index; 12498c2ecf20Sopenharmony_ci filter->flags |= ENETC_PSFP_FLAGS_FMI; 12508c2ecf20Sopenharmony_ci filter->fmi_index = fmi->index; 12518c2ecf20Sopenharmony_ci sfi->meter_id = fmi->index; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (entryp->police.mtu) 12558c2ecf20Sopenharmony_ci sfi->maxsdu = entryp->police.mtu; 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci /* prio ref the filter prio */ 12598c2ecf20Sopenharmony_ci if (f->common.prio && f->common.prio <= BIT(3)) 12608c2ecf20Sopenharmony_ci sfi->prio = f->common.prio - 1; 12618c2ecf20Sopenharmony_ci else 12628c2ecf20Sopenharmony_ci sfi->prio = ENETC_PSFP_WILDCARD; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci old_sfi = enetc_psfp_check_sfi(sfi); 12658c2ecf20Sopenharmony_ci if (!old_sfi) { 12668c2ecf20Sopenharmony_ci int index; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci index = enetc_get_free_index(priv); 12698c2ecf20Sopenharmony_ci if (index < 0) { 12708c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No Stream Filter resource!"); 12718c2ecf20Sopenharmony_ci err = -ENOSPC; 12728c2ecf20Sopenharmony_ci goto free_fmi; 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci sfi->index = index; 12768c2ecf20Sopenharmony_ci sfi->handle = index + HANDLE_OFFSET; 12778c2ecf20Sopenharmony_ci /* Update the stream filter handle also */ 12788c2ecf20Sopenharmony_ci filter->sid.handle = sfi->handle; 12798c2ecf20Sopenharmony_ci filter->sfi_index = sfi->index; 12808c2ecf20Sopenharmony_ci sfi_overwrite = 0; 12818c2ecf20Sopenharmony_ci } else { 12828c2ecf20Sopenharmony_ci filter->sfi_index = old_sfi->index; 12838c2ecf20Sopenharmony_ci filter->sid.handle = old_sfi->handle; 12848c2ecf20Sopenharmony_ci sfi_overwrite = 1; 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci err = enetc_psfp_hw_set(priv, &filter->sid, 12888c2ecf20Sopenharmony_ci sfi_overwrite ? NULL : sfi, sgi, fmi); 12898c2ecf20Sopenharmony_ci if (err) 12908c2ecf20Sopenharmony_ci goto free_fmi; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci spin_lock(&epsfp.psfp_lock); 12938c2ecf20Sopenharmony_ci if (filter->flags & ENETC_PSFP_FLAGS_FMI) { 12948c2ecf20Sopenharmony_ci old_fmi = enetc_get_meter_by_index(filter->fmi_index); 12958c2ecf20Sopenharmony_ci if (old_fmi) { 12968c2ecf20Sopenharmony_ci fmi->refcount = old_fmi->refcount; 12978c2ecf20Sopenharmony_ci refcount_set(&fmi->refcount, 12988c2ecf20Sopenharmony_ci refcount_read(&old_fmi->refcount) + 1); 12998c2ecf20Sopenharmony_ci hlist_del(&old_fmi->node); 13008c2ecf20Sopenharmony_ci kfree(old_fmi); 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci hlist_add_head(&fmi->node, &epsfp.psfp_meter_list); 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci /* Remove the old node if exist and update with a new node */ 13068c2ecf20Sopenharmony_ci old_sgi = enetc_get_gate_by_index(filter->sgi_index); 13078c2ecf20Sopenharmony_ci if (old_sgi) { 13088c2ecf20Sopenharmony_ci refcount_set(&sgi->refcount, 13098c2ecf20Sopenharmony_ci refcount_read(&old_sgi->refcount) + 1); 13108c2ecf20Sopenharmony_ci hlist_del(&old_sgi->node); 13118c2ecf20Sopenharmony_ci kfree(old_sgi); 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci hlist_add_head(&sgi->node, &epsfp.psfp_gate_list); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (!old_sfi) { 13178c2ecf20Sopenharmony_ci hlist_add_head(&sfi->node, &epsfp.psfp_filter_list); 13188c2ecf20Sopenharmony_ci set_bit(sfi->index, epsfp.psfp_sfi_bitmap); 13198c2ecf20Sopenharmony_ci } else { 13208c2ecf20Sopenharmony_ci kfree(sfi); 13218c2ecf20Sopenharmony_ci refcount_inc(&old_sfi->refcount); 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci old_filter = enetc_get_stream_by_index(filter->sid.index); 13258c2ecf20Sopenharmony_ci if (old_filter) 13268c2ecf20Sopenharmony_ci remove_one_chain(priv, old_filter); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci filter->stats.lastused = jiffies; 13298c2ecf20Sopenharmony_ci hlist_add_head(&filter->node, &epsfp.stream_list); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci spin_unlock(&epsfp.psfp_lock); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci return 0; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cifree_fmi: 13368c2ecf20Sopenharmony_ci kfree(fmi); 13378c2ecf20Sopenharmony_cifree_sfi: 13388c2ecf20Sopenharmony_ci kfree(sfi); 13398c2ecf20Sopenharmony_cifree_gate: 13408c2ecf20Sopenharmony_ci kfree(sgi); 13418c2ecf20Sopenharmony_cifree_filter: 13428c2ecf20Sopenharmony_ci kfree(filter); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return err; 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic int enetc_config_clsflower(struct enetc_ndev_priv *priv, 13488c2ecf20Sopenharmony_ci struct flow_cls_offload *cls_flower) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci struct flow_rule *rule = flow_cls_offload_flow_rule(cls_flower); 13518c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack = cls_flower->common.extack; 13528c2ecf20Sopenharmony_ci struct flow_dissector *dissector = rule->match.dissector; 13538c2ecf20Sopenharmony_ci struct flow_action *action = &rule->action; 13548c2ecf20Sopenharmony_ci struct flow_action_entry *entry; 13558c2ecf20Sopenharmony_ci struct actions_fwd *fwd; 13568c2ecf20Sopenharmony_ci u64 actions = 0; 13578c2ecf20Sopenharmony_ci int i, err; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci if (!flow_action_has_entries(action)) { 13608c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "At least one action is needed"); 13618c2ecf20Sopenharmony_ci return -EINVAL; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci flow_action_for_each(i, entry, action) 13658c2ecf20Sopenharmony_ci actions |= BIT(entry->id); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci fwd = enetc_check_flow_actions(actions, dissector->used_keys); 13688c2ecf20Sopenharmony_ci if (!fwd) { 13698c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported filter type!"); 13708c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (fwd->output & FILTER_ACTION_TYPE_PSFP) { 13748c2ecf20Sopenharmony_ci err = enetc_psfp_parse_clsflower(priv, cls_flower); 13758c2ecf20Sopenharmony_ci if (err) { 13768c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid PSFP inputs"); 13778c2ecf20Sopenharmony_ci return err; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci } else { 13808c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported actions"); 13818c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci return 0; 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_cistatic int enetc_psfp_destroy_clsflower(struct enetc_ndev_priv *priv, 13888c2ecf20Sopenharmony_ci struct flow_cls_offload *f) 13898c2ecf20Sopenharmony_ci{ 13908c2ecf20Sopenharmony_ci struct enetc_stream_filter *filter; 13918c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack = f->common.extack; 13928c2ecf20Sopenharmony_ci int err; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci if (f->common.chain_index >= priv->psfp_cap.max_streamid) { 13958c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No Stream identify resource!"); 13968c2ecf20Sopenharmony_ci return -ENOSPC; 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci filter = enetc_get_stream_by_index(f->common.chain_index); 14008c2ecf20Sopenharmony_ci if (!filter) 14018c2ecf20Sopenharmony_ci return -EINVAL; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci err = enetc_streamid_hw_set(priv, &filter->sid, false); 14048c2ecf20Sopenharmony_ci if (err) 14058c2ecf20Sopenharmony_ci return err; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci remove_one_chain(priv, filter); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci return 0; 14108c2ecf20Sopenharmony_ci} 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_cistatic int enetc_destroy_clsflower(struct enetc_ndev_priv *priv, 14138c2ecf20Sopenharmony_ci struct flow_cls_offload *f) 14148c2ecf20Sopenharmony_ci{ 14158c2ecf20Sopenharmony_ci return enetc_psfp_destroy_clsflower(priv, f); 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic int enetc_psfp_get_stats(struct enetc_ndev_priv *priv, 14198c2ecf20Sopenharmony_ci struct flow_cls_offload *f) 14208c2ecf20Sopenharmony_ci{ 14218c2ecf20Sopenharmony_ci struct psfp_streamfilter_counters counters = {}; 14228c2ecf20Sopenharmony_ci struct enetc_stream_filter *filter; 14238c2ecf20Sopenharmony_ci struct flow_stats stats = {}; 14248c2ecf20Sopenharmony_ci int err; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci filter = enetc_get_stream_by_index(f->common.chain_index); 14278c2ecf20Sopenharmony_ci if (!filter) 14288c2ecf20Sopenharmony_ci return -EINVAL; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci err = enetc_streamcounter_hw_get(priv, filter->sfi_index, &counters); 14318c2ecf20Sopenharmony_ci if (err) 14328c2ecf20Sopenharmony_ci return -EINVAL; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci spin_lock(&epsfp.psfp_lock); 14358c2ecf20Sopenharmony_ci stats.pkts = counters.matching_frames_count + 14368c2ecf20Sopenharmony_ci counters.not_passing_sdu_count - 14378c2ecf20Sopenharmony_ci filter->stats.pkts; 14388c2ecf20Sopenharmony_ci stats.drops = counters.not_passing_frames_count + 14398c2ecf20Sopenharmony_ci counters.not_passing_sdu_count + 14408c2ecf20Sopenharmony_ci counters.red_frames_count - 14418c2ecf20Sopenharmony_ci filter->stats.drops; 14428c2ecf20Sopenharmony_ci stats.lastused = filter->stats.lastused; 14438c2ecf20Sopenharmony_ci filter->stats.pkts += stats.pkts; 14448c2ecf20Sopenharmony_ci filter->stats.drops += stats.drops; 14458c2ecf20Sopenharmony_ci spin_unlock(&epsfp.psfp_lock); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci flow_stats_update(&f->stats, 0x0, stats.pkts, stats.drops, 14488c2ecf20Sopenharmony_ci stats.lastused, FLOW_ACTION_HW_STATS_DELAYED); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_cistatic int enetc_setup_tc_cls_flower(struct enetc_ndev_priv *priv, 14548c2ecf20Sopenharmony_ci struct flow_cls_offload *cls_flower) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci switch (cls_flower->command) { 14578c2ecf20Sopenharmony_ci case FLOW_CLS_REPLACE: 14588c2ecf20Sopenharmony_ci return enetc_config_clsflower(priv, cls_flower); 14598c2ecf20Sopenharmony_ci case FLOW_CLS_DESTROY: 14608c2ecf20Sopenharmony_ci return enetc_destroy_clsflower(priv, cls_flower); 14618c2ecf20Sopenharmony_ci case FLOW_CLS_STATS: 14628c2ecf20Sopenharmony_ci return enetc_psfp_get_stats(priv, cls_flower); 14638c2ecf20Sopenharmony_ci default: 14648c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_cistatic inline void clean_psfp_sfi_bitmap(void) 14698c2ecf20Sopenharmony_ci{ 14708c2ecf20Sopenharmony_ci bitmap_free(epsfp.psfp_sfi_bitmap); 14718c2ecf20Sopenharmony_ci epsfp.psfp_sfi_bitmap = NULL; 14728c2ecf20Sopenharmony_ci} 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_cistatic void clean_stream_list(void) 14758c2ecf20Sopenharmony_ci{ 14768c2ecf20Sopenharmony_ci struct enetc_stream_filter *s; 14778c2ecf20Sopenharmony_ci struct hlist_node *tmp; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(s, tmp, &epsfp.stream_list, node) { 14808c2ecf20Sopenharmony_ci hlist_del(&s->node); 14818c2ecf20Sopenharmony_ci kfree(s); 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_cistatic void clean_sfi_list(void) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci struct enetc_psfp_filter *sfi; 14888c2ecf20Sopenharmony_ci struct hlist_node *tmp; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(sfi, tmp, &epsfp.psfp_filter_list, node) { 14918c2ecf20Sopenharmony_ci hlist_del(&sfi->node); 14928c2ecf20Sopenharmony_ci kfree(sfi); 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci} 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_cistatic void clean_sgi_list(void) 14978c2ecf20Sopenharmony_ci{ 14988c2ecf20Sopenharmony_ci struct enetc_psfp_gate *sgi; 14998c2ecf20Sopenharmony_ci struct hlist_node *tmp; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(sgi, tmp, &epsfp.psfp_gate_list, node) { 15028c2ecf20Sopenharmony_ci hlist_del(&sgi->node); 15038c2ecf20Sopenharmony_ci kfree(sgi); 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci} 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_cistatic void clean_psfp_all(void) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci /* Disable all list nodes and free all memory */ 15108c2ecf20Sopenharmony_ci clean_sfi_list(); 15118c2ecf20Sopenharmony_ci clean_sgi_list(); 15128c2ecf20Sopenharmony_ci clean_stream_list(); 15138c2ecf20Sopenharmony_ci epsfp.dev_bitmap = 0; 15148c2ecf20Sopenharmony_ci clean_psfp_sfi_bitmap(); 15158c2ecf20Sopenharmony_ci} 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ciint enetc_setup_tc_block_cb(enum tc_setup_type type, void *type_data, 15188c2ecf20Sopenharmony_ci void *cb_priv) 15198c2ecf20Sopenharmony_ci{ 15208c2ecf20Sopenharmony_ci struct net_device *ndev = cb_priv; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci if (!tc_can_offload(ndev)) 15238c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci switch (type) { 15268c2ecf20Sopenharmony_ci case TC_SETUP_CLSFLOWER: 15278c2ecf20Sopenharmony_ci return enetc_setup_tc_cls_flower(netdev_priv(ndev), type_data); 15288c2ecf20Sopenharmony_ci default: 15298c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci} 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ciint enetc_set_psfp(struct net_device *ndev, bool en) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci struct enetc_ndev_priv *priv = netdev_priv(ndev); 15368c2ecf20Sopenharmony_ci int err; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci if (en) { 15398c2ecf20Sopenharmony_ci err = enetc_psfp_enable(priv); 15408c2ecf20Sopenharmony_ci if (err) 15418c2ecf20Sopenharmony_ci return err; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci priv->active_offloads |= ENETC_F_QCI; 15448c2ecf20Sopenharmony_ci return 0; 15458c2ecf20Sopenharmony_ci } 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci err = enetc_psfp_disable(priv); 15488c2ecf20Sopenharmony_ci if (err) 15498c2ecf20Sopenharmony_ci return err; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci priv->active_offloads &= ~ENETC_F_QCI; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci return 0; 15548c2ecf20Sopenharmony_ci} 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ciint enetc_psfp_init(struct enetc_ndev_priv *priv) 15578c2ecf20Sopenharmony_ci{ 15588c2ecf20Sopenharmony_ci if (epsfp.psfp_sfi_bitmap) 15598c2ecf20Sopenharmony_ci return 0; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci epsfp.psfp_sfi_bitmap = bitmap_zalloc(priv->psfp_cap.max_psfp_filter, 15628c2ecf20Sopenharmony_ci GFP_KERNEL); 15638c2ecf20Sopenharmony_ci if (!epsfp.psfp_sfi_bitmap) 15648c2ecf20Sopenharmony_ci return -ENOMEM; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci spin_lock_init(&epsfp.psfp_lock); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci if (list_empty(&enetc_block_cb_list)) 15698c2ecf20Sopenharmony_ci epsfp.dev_bitmap = 0; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci return 0; 15728c2ecf20Sopenharmony_ci} 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ciint enetc_psfp_clean(struct enetc_ndev_priv *priv) 15758c2ecf20Sopenharmony_ci{ 15768c2ecf20Sopenharmony_ci if (!list_empty(&enetc_block_cb_list)) 15778c2ecf20Sopenharmony_ci return -EBUSY; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci clean_psfp_all(); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci return 0; 15828c2ecf20Sopenharmony_ci} 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ciint enetc_setup_tc_psfp(struct net_device *ndev, void *type_data) 15858c2ecf20Sopenharmony_ci{ 15868c2ecf20Sopenharmony_ci struct enetc_ndev_priv *priv = netdev_priv(ndev); 15878c2ecf20Sopenharmony_ci struct flow_block_offload *f = type_data; 15888c2ecf20Sopenharmony_ci int err; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci err = flow_block_cb_setup_simple(f, &enetc_block_cb_list, 15918c2ecf20Sopenharmony_ci enetc_setup_tc_block_cb, 15928c2ecf20Sopenharmony_ci ndev, ndev, true); 15938c2ecf20Sopenharmony_ci if (err) 15948c2ecf20Sopenharmony_ci return err; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci switch (f->command) { 15978c2ecf20Sopenharmony_ci case FLOW_BLOCK_BIND: 15988c2ecf20Sopenharmony_ci set_bit(enetc_get_port(priv), &epsfp.dev_bitmap); 15998c2ecf20Sopenharmony_ci break; 16008c2ecf20Sopenharmony_ci case FLOW_BLOCK_UNBIND: 16018c2ecf20Sopenharmony_ci clear_bit(enetc_get_port(priv), &epsfp.dev_bitmap); 16028c2ecf20Sopenharmony_ci if (!epsfp.dev_bitmap) 16038c2ecf20Sopenharmony_ci clean_psfp_all(); 16048c2ecf20Sopenharmony_ci break; 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci return 0; 16088c2ecf20Sopenharmony_ci} 1609