LCOV - code coverage report
Current view: top level - drivers/net/ethernet/stmicro/stmmac - stmmac_tc.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 330 0.0 %
Date: 2022-04-01 14:35:51 Functions: 0 15 0.0 %
Branches: 0 243 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: (GPL-2.0 OR MIT)
       2                 :            : /*
       3                 :            :  * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
       4                 :            :  * stmmac TC Handling (HW only)
       5                 :            :  */
       6                 :            : 
       7                 :            : #include <net/pkt_cls.h>
       8                 :            : #include <net/tc_act/tc_gact.h>
       9                 :            : #include "common.h"
      10                 :            : #include "dwmac4.h"
      11                 :            : #include "dwmac5.h"
      12                 :            : #include "stmmac.h"
      13                 :            : 
      14                 :          0 : static void tc_fill_all_pass_entry(struct stmmac_tc_entry *entry)
      15                 :            : {
      16                 :          0 :         memset(entry, 0, sizeof(*entry));
      17                 :          0 :         entry->in_use = true;
      18                 :          0 :         entry->is_last = true;
      19                 :          0 :         entry->is_frag = false;
      20                 :          0 :         entry->prio = ~0x0;
      21                 :          0 :         entry->handle = 0;
      22                 :          0 :         entry->val.match_data = 0x0;
      23                 :          0 :         entry->val.match_en = 0x0;
      24                 :          0 :         entry->val.af = 1;
      25                 :          0 :         entry->val.dma_ch_no = 0x0;
      26                 :            : }
      27                 :            : 
      28                 :          0 : static struct stmmac_tc_entry *tc_find_entry(struct stmmac_priv *priv,
      29                 :            :                                              struct tc_cls_u32_offload *cls,
      30                 :            :                                              bool free)
      31                 :            : {
      32                 :          0 :         struct stmmac_tc_entry *entry, *first = NULL, *dup = NULL;
      33                 :          0 :         u32 loc = cls->knode.handle;
      34                 :          0 :         int i;
      35                 :            : 
      36         [ #  # ]:          0 :         for (i = 0; i < priv->tc_entries_max; i++) {
      37                 :          0 :                 entry = &priv->tc_entries[i];
      38   [ #  #  #  #  :          0 :                 if (!entry->in_use && !first && free)
                   #  # ]
      39                 :          0 :                         first = entry;
      40   [ #  #  #  #  :          0 :                 if ((entry->handle == loc) && !free && !entry->is_frag)
                   #  # ]
      41                 :          0 :                         dup = entry;
      42                 :            :         }
      43                 :            : 
      44         [ #  # ]:          0 :         if (dup)
      45                 :            :                 return dup;
      46         [ #  # ]:          0 :         if (first) {
      47                 :          0 :                 first->handle = loc;
      48                 :          0 :                 first->in_use = true;
      49                 :            : 
      50                 :            :                 /* Reset HW values */
      51                 :          0 :                 memset(&first->val, 0, sizeof(first->val));
      52                 :            :         }
      53                 :            : 
      54                 :            :         return first;
      55                 :            : }
      56                 :            : 
      57                 :            : static int tc_fill_actions(struct stmmac_tc_entry *entry,
      58                 :            :                            struct stmmac_tc_entry *frag,
      59                 :            :                            struct tc_cls_u32_offload *cls)
      60                 :            : {
      61                 :            :         struct stmmac_tc_entry *action_entry = entry;
      62                 :            :         const struct tc_action *act;
      63                 :            :         struct tcf_exts *exts;
      64                 :            :         int i;
      65                 :            : 
      66                 :            :         exts = cls->knode.exts;
      67                 :            :         if (!tcf_exts_has_actions(exts))
      68                 :            :                 return -EINVAL;
      69                 :            :         if (frag)
      70                 :            :                 action_entry = frag;
      71                 :            : 
      72                 :            :         tcf_exts_for_each_action(i, act, exts) {
      73                 :            :                 /* Accept */
      74                 :            :                 if (is_tcf_gact_ok(act)) {
      75                 :            :                         action_entry->val.af = 1;
      76                 :            :                         break;
      77                 :            :                 }
      78                 :            :                 /* Drop */
      79                 :            :                 if (is_tcf_gact_shot(act)) {
      80                 :            :                         action_entry->val.rf = 1;
      81                 :            :                         break;
      82                 :            :                 }
      83                 :            : 
      84                 :            :                 /* Unsupported */
      85                 :            :                 return -EINVAL;
      86                 :            :         }
      87                 :            : 
      88                 :            :         return 0;
      89                 :            : }
      90                 :            : 
      91                 :          0 : static int tc_fill_entry(struct stmmac_priv *priv,
      92                 :            :                          struct tc_cls_u32_offload *cls)
      93                 :            : {
      94                 :          0 :         struct stmmac_tc_entry *entry, *frag = NULL;
      95                 :          0 :         struct tc_u32_sel *sel = cls->knode.sel;
      96                 :          0 :         u32 off, data, mask, real_off, rem;
      97                 :          0 :         u32 prio = cls->common.prio << 16;
      98                 :          0 :         int ret;
      99                 :            : 
     100                 :            :         /* Only 1 match per entry */
     101         [ #  # ]:          0 :         if (sel->nkeys <= 0 || sel->nkeys > 1)
     102                 :            :                 return -EINVAL;
     103                 :            : 
     104                 :          0 :         off = sel->keys[0].off << sel->offshift;
     105                 :          0 :         data = sel->keys[0].val;
     106                 :          0 :         mask = sel->keys[0].mask;
     107                 :            : 
     108      [ #  #  # ]:          0 :         switch (ntohs(cls->common.protocol)) {
     109                 :            :         case ETH_P_ALL:
     110                 :            :                 break;
     111                 :          0 :         case ETH_P_IP:
     112                 :          0 :                 off += ETH_HLEN;
     113                 :          0 :                 break;
     114                 :            :         default:
     115                 :            :                 return -EINVAL;
     116                 :            :         }
     117                 :            : 
     118         [ #  # ]:          0 :         if (off > priv->tc_off_max)
     119                 :            :                 return -EINVAL;
     120                 :            : 
     121                 :          0 :         real_off = off / 4;
     122                 :          0 :         rem = off % 4;
     123                 :            : 
     124                 :          0 :         entry = tc_find_entry(priv, cls, true);
     125         [ #  # ]:          0 :         if (!entry)
     126                 :            :                 return -EINVAL;
     127                 :            : 
     128         [ #  # ]:          0 :         if (rem) {
     129                 :          0 :                 frag = tc_find_entry(priv, cls, true);
     130         [ #  # ]:          0 :                 if (!frag) {
     131                 :          0 :                         ret = -EINVAL;
     132                 :          0 :                         goto err_unuse;
     133                 :            :                 }
     134                 :            : 
     135                 :          0 :                 entry->frag_ptr = frag;
     136                 :          0 :                 entry->val.match_en = (mask << (rem * 8)) &
     137                 :          0 :                         GENMASK(31, rem * 8);
     138                 :          0 :                 entry->val.match_data = (data << (rem * 8)) &
     139                 :            :                         GENMASK(31, rem * 8);
     140                 :          0 :                 entry->val.frame_offset = real_off;
     141                 :          0 :                 entry->prio = prio;
     142                 :            : 
     143                 :          0 :                 frag->val.match_en = (mask >> (rem * 8)) &
     144                 :          0 :                         GENMASK(rem * 8 - 1, 0);
     145                 :          0 :                 frag->val.match_data = (data >> (rem * 8)) &
     146                 :            :                         GENMASK(rem * 8 - 1, 0);
     147                 :          0 :                 frag->val.frame_offset = real_off + 1;
     148                 :          0 :                 frag->prio = prio;
     149                 :          0 :                 frag->is_frag = true;
     150                 :            :         } else {
     151                 :          0 :                 entry->frag_ptr = NULL;
     152                 :          0 :                 entry->val.match_en = mask;
     153                 :          0 :                 entry->val.match_data = data;
     154                 :          0 :                 entry->val.frame_offset = real_off;
     155                 :          0 :                 entry->prio = prio;
     156                 :            :         }
     157                 :            : 
     158                 :          0 :         ret = tc_fill_actions(entry, frag, cls);
     159         [ #  # ]:          0 :         if (ret)
     160                 :          0 :                 goto err_unuse;
     161                 :            : 
     162                 :            :         return 0;
     163                 :            : 
     164                 :          0 : err_unuse:
     165         [ #  # ]:          0 :         if (frag)
     166                 :          0 :                 frag->in_use = false;
     167                 :          0 :         entry->in_use = false;
     168                 :          0 :         return ret;
     169                 :            : }
     170                 :            : 
     171                 :          0 : static void tc_unfill_entry(struct stmmac_priv *priv,
     172                 :            :                             struct tc_cls_u32_offload *cls)
     173                 :            : {
     174                 :          0 :         struct stmmac_tc_entry *entry;
     175                 :            : 
     176                 :          0 :         entry = tc_find_entry(priv, cls, false);
     177         [ #  # ]:          0 :         if (!entry)
     178                 :            :                 return;
     179                 :            : 
     180                 :          0 :         entry->in_use = false;
     181         [ #  # ]:          0 :         if (entry->frag_ptr) {
     182                 :          0 :                 entry = entry->frag_ptr;
     183                 :          0 :                 entry->is_frag = false;
     184                 :          0 :                 entry->in_use = false;
     185                 :            :         }
     186                 :            : }
     187                 :            : 
     188                 :          0 : static int tc_config_knode(struct stmmac_priv *priv,
     189                 :            :                            struct tc_cls_u32_offload *cls)
     190                 :            : {
     191                 :          0 :         int ret;
     192                 :            : 
     193                 :          0 :         ret = tc_fill_entry(priv, cls);
     194         [ #  # ]:          0 :         if (ret)
     195                 :            :                 return ret;
     196                 :            : 
     197   [ #  #  #  # ]:          0 :         ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
     198                 :            :                         priv->tc_entries_max);
     199         [ #  # ]:          0 :         if (ret)
     200                 :          0 :                 goto err_unfill;
     201                 :            : 
     202                 :            :         return 0;
     203                 :            : 
     204                 :            : err_unfill:
     205                 :          0 :         tc_unfill_entry(priv, cls);
     206                 :          0 :         return ret;
     207                 :            : }
     208                 :            : 
     209                 :          0 : static int tc_delete_knode(struct stmmac_priv *priv,
     210                 :            :                            struct tc_cls_u32_offload *cls)
     211                 :            : {
     212                 :          0 :         int ret;
     213                 :            : 
     214                 :            :         /* Set entry and fragments as not used */
     215                 :          0 :         tc_unfill_entry(priv, cls);
     216                 :            : 
     217   [ #  #  #  # ]:          0 :         ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
     218                 :            :                         priv->tc_entries_max);
     219         [ #  # ]:          0 :         if (ret)
     220                 :          0 :                 return ret;
     221                 :            : 
     222                 :            :         return 0;
     223                 :            : }
     224                 :            : 
     225                 :          0 : static int tc_setup_cls_u32(struct stmmac_priv *priv,
     226                 :            :                             struct tc_cls_u32_offload *cls)
     227                 :            : {
     228   [ #  #  #  # ]:          0 :         switch (cls->command) {
     229                 :          0 :         case TC_CLSU32_REPLACE_KNODE:
     230                 :          0 :                 tc_unfill_entry(priv, cls);
     231                 :            :                 /* Fall through */
     232                 :          0 :         case TC_CLSU32_NEW_KNODE:
     233                 :          0 :                 return tc_config_knode(priv, cls);
     234                 :          0 :         case TC_CLSU32_DELETE_KNODE:
     235                 :          0 :                 return tc_delete_knode(priv, cls);
     236                 :            :         default:
     237                 :            :                 return -EOPNOTSUPP;
     238                 :            :         }
     239                 :            : }
     240                 :            : 
     241                 :          0 : static int tc_init(struct stmmac_priv *priv)
     242                 :            : {
     243                 :          0 :         struct dma_features *dma_cap = &priv->dma_cap;
     244                 :          0 :         unsigned int count;
     245                 :          0 :         int i;
     246                 :            : 
     247         [ #  # ]:          0 :         if (dma_cap->l3l4fnum) {
     248                 :          0 :                 priv->flow_entries_max = dma_cap->l3l4fnum;
     249                 :          0 :                 priv->flow_entries = devm_kcalloc(priv->device,
     250                 :            :                                                   dma_cap->l3l4fnum,
     251                 :            :                                                   sizeof(*priv->flow_entries),
     252                 :            :                                                   GFP_KERNEL);
     253         [ #  # ]:          0 :                 if (!priv->flow_entries)
     254                 :            :                         return -ENOMEM;
     255                 :            : 
     256         [ #  # ]:          0 :                 for (i = 0; i < priv->flow_entries_max; i++)
     257                 :          0 :                         priv->flow_entries[i].idx = i;
     258                 :            : 
     259                 :          0 :                 dev_info(priv->device, "Enabled Flow TC (entries=%d)\n",
     260                 :            :                          priv->flow_entries_max);
     261                 :            :         }
     262                 :            : 
     263                 :            :         /* Fail silently as we can still use remaining features, e.g. CBS */
     264         [ #  # ]:          0 :         if (!dma_cap->frpsel)
     265                 :            :                 return 0;
     266                 :            : 
     267   [ #  #  #  # ]:          0 :         switch (dma_cap->frpbs) {
     268                 :          0 :         case 0x0:
     269                 :          0 :                 priv->tc_off_max = 64;
     270                 :          0 :                 break;
     271                 :          0 :         case 0x1:
     272                 :          0 :                 priv->tc_off_max = 128;
     273                 :          0 :                 break;
     274                 :          0 :         case 0x2:
     275                 :          0 :                 priv->tc_off_max = 256;
     276                 :          0 :                 break;
     277                 :            :         default:
     278                 :            :                 return -EINVAL;
     279                 :            :         }
     280                 :            : 
     281         [ #  # ]:          0 :         switch (dma_cap->frpes) {
     282                 :            :         case 0x0:
     283                 :            :                 count = 64;
     284                 :            :                 break;
     285                 :            :         case 0x1:
     286                 :            :                 count = 128;
     287                 :            :                 break;
     288                 :            :         case 0x2:
     289                 :            :                 count = 256;
     290                 :            :                 break;
     291                 :            :         default:
     292                 :            :                 return -EINVAL;
     293                 :            :         }
     294                 :            : 
     295                 :            :         /* Reserve one last filter which lets all pass */
     296                 :          0 :         priv->tc_entries_max = count;
     297                 :          0 :         priv->tc_entries = devm_kcalloc(priv->device,
     298                 :            :                         count, sizeof(*priv->tc_entries), GFP_KERNEL);
     299         [ #  # ]:          0 :         if (!priv->tc_entries)
     300                 :            :                 return -ENOMEM;
     301                 :            : 
     302                 :          0 :         tc_fill_all_pass_entry(&priv->tc_entries[count - 1]);
     303                 :            : 
     304                 :          0 :         dev_info(priv->device, "Enabling HW TC (entries=%d, max_off=%d)\n",
     305                 :            :                         priv->tc_entries_max, priv->tc_off_max);
     306                 :          0 :         return 0;
     307                 :            : }
     308                 :            : 
     309                 :          0 : static int tc_setup_cbs(struct stmmac_priv *priv,
     310                 :            :                         struct tc_cbs_qopt_offload *qopt)
     311                 :            : {
     312                 :          0 :         u32 tx_queues_count = priv->plat->tx_queues_to_use;
     313                 :          0 :         u32 queue = qopt->queue;
     314                 :          0 :         u32 ptr, speed_div;
     315                 :          0 :         u32 mode_to_use;
     316                 :          0 :         u64 value;
     317                 :          0 :         int ret;
     318                 :            : 
     319                 :            :         /* Queue 0 is not AVB capable */
     320   [ #  #  #  # ]:          0 :         if (queue <= 0 || queue >= tx_queues_count)
     321                 :            :                 return -EINVAL;
     322         [ #  # ]:          0 :         if (!priv->dma_cap.av)
     323                 :            :                 return -EOPNOTSUPP;
     324                 :            : 
     325                 :          0 :         mode_to_use = priv->plat->tx_queues_cfg[queue].mode_to_use;
     326   [ #  #  #  # ]:          0 :         if (mode_to_use == MTL_QUEUE_DCB && qopt->enable) {
     327   [ #  #  #  # ]:          0 :                 ret = stmmac_dma_qmode(priv, priv->ioaddr, queue, MTL_QUEUE_AVB);
     328                 :          0 :                 if (ret)
     329                 :            :                         return ret;
     330                 :            : 
     331                 :          0 :                 priv->plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_AVB;
     332         [ #  # ]:          0 :         } else if (!qopt->enable) {
     333   [ #  #  #  # ]:          0 :                 return stmmac_dma_qmode(priv, priv->ioaddr, queue, MTL_QUEUE_DCB);
     334                 :            :         }
     335                 :            : 
     336                 :            :         /* Port Transmit Rate and Speed Divider */
     337         [ #  # ]:          0 :         ptr = (priv->speed == SPEED_100) ? 4 : 8;
     338         [ #  # ]:          0 :         speed_div = (priv->speed == SPEED_100) ? 100000 : 1000000;
     339                 :            : 
     340                 :            :         /* Final adjustments for HW */
     341         [ #  # ]:          0 :         value = div_s64(qopt->idleslope * 1024ll * ptr, speed_div);
     342                 :          0 :         priv->plat->tx_queues_cfg[queue].idle_slope = value & GENMASK(31, 0);
     343                 :            : 
     344                 :          0 :         value = div_s64(-qopt->sendslope * 1024ll * ptr, speed_div);
     345                 :          0 :         priv->plat->tx_queues_cfg[queue].send_slope = value & GENMASK(31, 0);
     346                 :            : 
     347                 :          0 :         value = qopt->hicredit * 1024ll * 8;
     348                 :          0 :         priv->plat->tx_queues_cfg[queue].high_credit = value & GENMASK(31, 0);
     349                 :            : 
     350                 :          0 :         value = qopt->locredit * 1024ll * 8;
     351                 :          0 :         priv->plat->tx_queues_cfg[queue].low_credit = value & GENMASK(31, 0);
     352                 :            : 
     353   [ #  #  #  # ]:          0 :         ret = stmmac_config_cbs(priv, priv->hw,
     354                 :            :                                 priv->plat->tx_queues_cfg[queue].send_slope,
     355                 :            :                                 priv->plat->tx_queues_cfg[queue].idle_slope,
     356                 :            :                                 priv->plat->tx_queues_cfg[queue].high_credit,
     357                 :            :                                 priv->plat->tx_queues_cfg[queue].low_credit,
     358                 :            :                                 queue);
     359                 :          0 :         if (ret)
     360                 :            :                 return ret;
     361                 :            : 
     362                 :          0 :         dev_info(priv->device, "CBS queue %d: send %d, idle %d, hi %d, lo %d\n",
     363                 :            :                         queue, qopt->sendslope, qopt->idleslope,
     364                 :            :                         qopt->hicredit, qopt->locredit);
     365                 :          0 :         return 0;
     366                 :            : }
     367                 :            : 
     368                 :          0 : static int tc_parse_flow_actions(struct stmmac_priv *priv,
     369                 :            :                                  struct flow_action *action,
     370                 :            :                                  struct stmmac_flow_entry *entry)
     371                 :            : {
     372                 :          0 :         struct flow_action_entry *act;
     373                 :          0 :         int i;
     374                 :            : 
     375                 :          0 :         if (!flow_action_has_entries(action))
     376                 :            :                 return -EINVAL;
     377                 :            : 
     378         [ #  # ]:          0 :         flow_action_for_each(i, act, action) {
     379         [ #  # ]:          0 :                 switch (act->id) {
     380                 :          0 :                 case FLOW_ACTION_DROP:
     381                 :          0 :                         entry->action |= STMMAC_FLOW_ACTION_DROP;
     382                 :          0 :                         return 0;
     383                 :            :                 default:
     384                 :          0 :                         break;
     385                 :            :                 }
     386                 :            :         }
     387                 :            : 
     388                 :            :         /* Nothing to do, maybe inverse filter ? */
     389                 :            :         return 0;
     390                 :            : }
     391                 :            : 
     392                 :          0 : static int tc_add_basic_flow(struct stmmac_priv *priv,
     393                 :            :                              struct flow_cls_offload *cls,
     394                 :            :                              struct stmmac_flow_entry *entry)
     395                 :            : {
     396         [ #  # ]:          0 :         struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
     397                 :          0 :         struct flow_dissector *dissector = rule->match.dissector;
     398                 :          0 :         struct flow_match_basic match;
     399                 :            : 
     400                 :            :         /* Nothing to do here */
     401         [ #  # ]:          0 :         if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_BASIC))
     402                 :            :                 return -EINVAL;
     403                 :            : 
     404                 :          0 :         flow_rule_match_basic(rule, &match);
     405                 :          0 :         entry->ip_proto = match.key->ip_proto;
     406                 :          0 :         return 0;
     407                 :            : }
     408                 :            : 
     409                 :          0 : static int tc_add_ip4_flow(struct stmmac_priv *priv,
     410                 :            :                            struct flow_cls_offload *cls,
     411                 :            :                            struct stmmac_flow_entry *entry)
     412                 :            : {
     413         [ #  # ]:          0 :         struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
     414                 :          0 :         struct flow_dissector *dissector = rule->match.dissector;
     415                 :          0 :         bool inv = entry->action & STMMAC_FLOW_ACTION_DROP;
     416                 :          0 :         struct flow_match_ipv4_addrs match;
     417                 :          0 :         u32 hw_match;
     418                 :          0 :         int ret;
     419                 :            : 
     420                 :            :         /* Nothing to do here */
     421         [ #  # ]:          0 :         if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS))
     422                 :            :                 return -EINVAL;
     423                 :            : 
     424                 :          0 :         flow_rule_match_ipv4_addrs(rule, &match);
     425                 :          0 :         hw_match = ntohl(match.key->src) & ntohl(match.mask->src);
     426         [ #  # ]:          0 :         if (hw_match) {
     427   [ #  #  #  # ]:          0 :                 ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true,
     428                 :            :                                               false, true, inv, hw_match);
     429         [ #  # ]:          0 :                 if (ret)
     430                 :          0 :                         return ret;
     431                 :            :         }
     432                 :            : 
     433                 :          0 :         hw_match = ntohl(match.key->dst) & ntohl(match.mask->dst);
     434         [ #  # ]:          0 :         if (hw_match) {
     435   [ #  #  #  # ]:          0 :                 ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true,
     436                 :            :                                               false, false, inv, hw_match);
     437         [ #  # ]:          0 :                 if (ret)
     438                 :          0 :                         return ret;
     439                 :            :         }
     440                 :            : 
     441                 :            :         return 0;
     442                 :            : }
     443                 :            : 
     444                 :          0 : static int tc_add_ports_flow(struct stmmac_priv *priv,
     445                 :            :                              struct flow_cls_offload *cls,
     446                 :            :                              struct stmmac_flow_entry *entry)
     447                 :            : {
     448         [ #  # ]:          0 :         struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
     449                 :          0 :         struct flow_dissector *dissector = rule->match.dissector;
     450                 :          0 :         bool inv = entry->action & STMMAC_FLOW_ACTION_DROP;
     451                 :          0 :         struct flow_match_ports match;
     452                 :          0 :         u32 hw_match;
     453                 :          0 :         bool is_udp;
     454                 :          0 :         int ret;
     455                 :            : 
     456                 :            :         /* Nothing to do here */
     457         [ #  # ]:          0 :         if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_PORTS))
     458                 :            :                 return -EINVAL;
     459                 :            : 
     460      [ #  #  # ]:          0 :         switch (entry->ip_proto) {
     461                 :            :         case IPPROTO_TCP:
     462                 :            :                 is_udp = false;
     463                 :            :                 break;
     464                 :          0 :         case IPPROTO_UDP:
     465                 :          0 :                 is_udp = true;
     466                 :          0 :                 break;
     467                 :            :         default:
     468                 :            :                 return -EINVAL;
     469                 :            :         }
     470                 :            : 
     471                 :          0 :         flow_rule_match_ports(rule, &match);
     472                 :            : 
     473                 :          0 :         hw_match = ntohs(match.key->src) & ntohs(match.mask->src);
     474         [ #  # ]:          0 :         if (hw_match) {
     475   [ #  #  #  # ]:          0 :                 ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, true,
     476                 :            :                                               is_udp, true, inv, hw_match);
     477         [ #  # ]:          0 :                 if (ret)
     478                 :          0 :                         return ret;
     479                 :            :         }
     480                 :            : 
     481                 :          0 :         hw_match = ntohs(match.key->dst) & ntohs(match.mask->dst);
     482         [ #  # ]:          0 :         if (hw_match) {
     483   [ #  #  #  # ]:          0 :                 ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, true,
     484                 :            :                                               is_udp, false, inv, hw_match);
     485         [ #  # ]:          0 :                 if (ret)
     486                 :          0 :                         return ret;
     487                 :            :         }
     488                 :            : 
     489                 :          0 :         entry->is_l4 = true;
     490                 :          0 :         return 0;
     491                 :            : }
     492                 :            : 
     493                 :          0 : static struct stmmac_flow_entry *tc_find_flow(struct stmmac_priv *priv,
     494                 :            :                                               struct flow_cls_offload *cls,
     495                 :            :                                               bool get_free)
     496                 :            : {
     497                 :          0 :         int i;
     498                 :            : 
     499   [ #  #  #  # ]:          0 :         for (i = 0; i < priv->flow_entries_max; i++) {
     500                 :          0 :                 struct stmmac_flow_entry *entry = &priv->flow_entries[i];
     501                 :            : 
     502   [ #  #  #  # ]:          0 :                 if (entry->cookie == cls->cookie)
     503                 :            :                         return entry;
     504         [ #  # ]:          0 :                 if (get_free && (entry->in_use == false))
     505                 :            :                         return entry;
     506                 :            :         }
     507                 :            : 
     508                 :            :         return NULL;
     509                 :            : }
     510                 :            : 
     511                 :            : static struct {
     512                 :            :         int (*fn)(struct stmmac_priv *priv, struct flow_cls_offload *cls,
     513                 :            :                   struct stmmac_flow_entry *entry);
     514                 :            : } tc_flow_parsers[] = {
     515                 :            :         { .fn = tc_add_basic_flow },
     516                 :            :         { .fn = tc_add_ip4_flow },
     517                 :            :         { .fn = tc_add_ports_flow },
     518                 :            : };
     519                 :            : 
     520                 :          0 : static int tc_add_flow(struct stmmac_priv *priv,
     521                 :            :                        struct flow_cls_offload *cls)
     522                 :            : {
     523                 :          0 :         struct stmmac_flow_entry *entry = tc_find_flow(priv, cls, false);
     524         [ #  # ]:          0 :         struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
     525                 :          0 :         int i, ret;
     526                 :            : 
     527         [ #  # ]:          0 :         if (!entry) {
     528                 :            :                 entry = tc_find_flow(priv, cls, true);
     529         [ #  # ]:          0 :                 if (!entry)
     530                 :            :                         return -ENOENT;
     531                 :            :         }
     532                 :            : 
     533         [ #  # ]:          0 :         ret = tc_parse_flow_actions(priv, &rule->action, entry);
     534                 :          0 :         if (ret)
     535                 :            :                 return ret;
     536                 :            : 
     537         [ #  # ]:          0 :         for (i = 0; i < ARRAY_SIZE(tc_flow_parsers); i++) {
     538                 :          0 :                 ret = tc_flow_parsers[i].fn(priv, cls, entry);
     539         [ #  # ]:          0 :                 if (!ret) {
     540                 :          0 :                         entry->in_use = true;
     541                 :          0 :                         continue;
     542                 :            :                 }
     543                 :            :         }
     544                 :            : 
     545         [ #  # ]:          0 :         if (!entry->in_use)
     546                 :            :                 return -EINVAL;
     547                 :            : 
     548                 :          0 :         entry->cookie = cls->cookie;
     549                 :          0 :         return 0;
     550                 :            : }
     551                 :            : 
     552                 :            : static int tc_del_flow(struct stmmac_priv *priv,
     553                 :            :                        struct flow_cls_offload *cls)
     554                 :            : {
     555                 :            :         struct stmmac_flow_entry *entry = tc_find_flow(priv, cls, false);
     556                 :            :         int ret;
     557                 :            : 
     558                 :            :         if (!entry || !entry->in_use)
     559                 :            :                 return -ENOENT;
     560                 :            : 
     561                 :            :         if (entry->is_l4) {
     562                 :            :                 ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, false,
     563                 :            :                                               false, false, false, 0);
     564                 :            :         } else {
     565                 :            :                 ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, false,
     566                 :            :                                               false, false, false, 0);
     567                 :            :         }
     568                 :            : 
     569                 :            :         entry->in_use = false;
     570                 :            :         entry->cookie = 0;
     571                 :            :         entry->is_l4 = false;
     572                 :            :         return ret;
     573                 :            : }
     574                 :            : 
     575                 :          0 : static int tc_setup_cls(struct stmmac_priv *priv,
     576                 :            :                         struct flow_cls_offload *cls)
     577                 :            : {
     578                 :          0 :         int ret = 0;
     579                 :            : 
     580                 :            :         /* When RSS is enabled, the filtering will be bypassed */
     581         [ #  # ]:          0 :         if (priv->rss.enable)
     582                 :            :                 return -EBUSY;
     583                 :            : 
     584      [ #  #  # ]:          0 :         switch (cls->command) {
     585                 :          0 :         case FLOW_CLS_REPLACE:
     586                 :          0 :                 ret = tc_add_flow(priv, cls);
     587                 :          0 :                 break;
     588                 :          0 :         case FLOW_CLS_DESTROY:
     589                 :          0 :                 ret = tc_del_flow(priv, cls);
     590                 :          0 :                 break;
     591                 :            :         default:
     592                 :            :                 return -EOPNOTSUPP;
     593                 :            :         }
     594                 :            : 
     595                 :            :         return ret;
     596                 :            : }
     597                 :            : 
     598                 :          0 : static int tc_setup_taprio(struct stmmac_priv *priv,
     599                 :            :                            struct tc_taprio_qopt_offload *qopt)
     600                 :            : {
     601                 :          0 :         u32 size, wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep;
     602                 :          0 :         struct plat_stmmacenet_data *plat = priv->plat;
     603                 :          0 :         struct timespec64 time;
     604                 :          0 :         bool fpe = false;
     605                 :          0 :         int i, ret = 0;
     606                 :          0 :         u64 ctr;
     607                 :            : 
     608         [ #  # ]:          0 :         if (!priv->dma_cap.estsel)
     609                 :            :                 return -EOPNOTSUPP;
     610                 :            : 
     611         [ #  # ]:          0 :         switch (wid) {
     612                 :            :         case 0x1:
     613                 :            :                 wid = 16;
     614                 :            :                 break;
     615                 :            :         case 0x2:
     616                 :            :                 wid = 20;
     617                 :            :                 break;
     618                 :            :         case 0x3:
     619                 :            :                 wid = 24;
     620                 :            :                 break;
     621                 :            :         default:
     622                 :            :                 return -EOPNOTSUPP;
     623                 :            :         }
     624                 :            : 
     625         [ #  # ]:          0 :         switch (dep) {
     626                 :            :         case 0x1:
     627                 :            :                 dep = 64;
     628                 :            :                 break;
     629                 :            :         case 0x2:
     630                 :            :                 dep = 128;
     631                 :            :                 break;
     632                 :            :         case 0x3:
     633                 :            :                 dep = 256;
     634                 :            :                 break;
     635                 :            :         case 0x4:
     636                 :            :                 dep = 512;
     637                 :            :                 break;
     638                 :            :         case 0x5:
     639                 :            :                 dep = 1024;
     640                 :            :                 break;
     641                 :            :         default:
     642                 :            :                 return -EOPNOTSUPP;
     643                 :            :         }
     644                 :            : 
     645         [ #  # ]:          0 :         if (!qopt->enable)
     646                 :          0 :                 goto disable;
     647         [ #  # ]:          0 :         if (qopt->num_entries >= dep)
     648                 :            :                 return -EINVAL;
     649         [ #  # ]:          0 :         if (!qopt->base_time)
     650                 :            :                 return -ERANGE;
     651         [ #  # ]:          0 :         if (!qopt->cycle_time)
     652                 :            :                 return -ERANGE;
     653                 :            : 
     654         [ #  # ]:          0 :         if (!plat->est) {
     655                 :          0 :                 plat->est = devm_kzalloc(priv->device, sizeof(*plat->est),
     656                 :            :                                          GFP_KERNEL);
     657         [ #  # ]:          0 :                 if (!plat->est)
     658                 :            :                         return -ENOMEM;
     659                 :            :         } else {
     660                 :          0 :                 memset(plat->est, 0, sizeof(*plat->est));
     661                 :            :         }
     662                 :            : 
     663                 :          0 :         size = qopt->num_entries;
     664                 :            : 
     665                 :          0 :         priv->plat->est->gcl_size = size;
     666                 :          0 :         priv->plat->est->enable = qopt->enable;
     667                 :            : 
     668         [ #  # ]:          0 :         for (i = 0; i < size; i++) {
     669                 :          0 :                 s64 delta_ns = qopt->entries[i].interval;
     670                 :          0 :                 u32 gates = qopt->entries[i].gate_mask;
     671                 :            : 
     672         [ #  # ]:          0 :                 if (delta_ns > GENMASK(wid, 0))
     673                 :            :                         return -ERANGE;
     674         [ #  # ]:          0 :                 if (gates > GENMASK(31 - wid, 0))
     675                 :            :                         return -ERANGE;
     676                 :            : 
     677   [ #  #  #  # ]:          0 :                 switch (qopt->entries[i].command) {
     678                 :          0 :                 case TC_TAPRIO_CMD_SET_GATES:
     679         [ #  # ]:          0 :                         if (fpe)
     680                 :            :                                 return -EINVAL;
     681                 :            :                         break;
     682                 :          0 :                 case TC_TAPRIO_CMD_SET_AND_HOLD:
     683                 :          0 :                         gates |= BIT(0);
     684                 :          0 :                         fpe = true;
     685                 :          0 :                         break;
     686                 :          0 :                 case TC_TAPRIO_CMD_SET_AND_RELEASE:
     687                 :          0 :                         gates &= ~BIT(0);
     688                 :          0 :                         fpe = true;
     689                 :          0 :                         break;
     690                 :            :                 default:
     691                 :            :                         return -EOPNOTSUPP;
     692                 :            :                 }
     693                 :            : 
     694                 :          0 :                 priv->plat->est->gcl[i] = delta_ns | (gates << wid);
     695                 :            :         }
     696                 :            : 
     697                 :            :         /* Adjust for real system time */
     698                 :          0 :         time = ktime_to_timespec64(qopt->base_time);
     699                 :          0 :         priv->plat->est->btr[0] = (u32)time.tv_nsec;
     700                 :          0 :         priv->plat->est->btr[1] = (u32)time.tv_sec;
     701                 :            : 
     702                 :          0 :         ctr = qopt->cycle_time;
     703                 :          0 :         priv->plat->est->ctr[0] = do_div(ctr, NSEC_PER_SEC);
     704                 :          0 :         priv->plat->est->ctr[1] = (u32)ctr;
     705                 :            : 
     706   [ #  #  #  # ]:          0 :         if (fpe && !priv->dma_cap.fpesel)
     707                 :            :                 return -EOPNOTSUPP;
     708                 :            : 
     709   [ #  #  #  # ]:          0 :         ret = stmmac_fpe_configure(priv, priv->ioaddr,
     710                 :            :                                    priv->plat->tx_queues_to_use,
     711                 :            :                                    priv->plat->rx_queues_to_use, fpe);
     712         [ #  # ]:          0 :         if (ret && fpe) {
     713                 :          0 :                 netdev_err(priv->dev, "failed to enable Frame Preemption\n");
     714                 :          0 :                 return ret;
     715                 :            :         }
     716                 :            : 
     717   [ #  #  #  # ]:          0 :         ret = stmmac_est_configure(priv, priv->ioaddr, priv->plat->est,
     718                 :            :                                    priv->plat->clk_ptp_rate);
     719         [ #  # ]:          0 :         if (ret) {
     720                 :          0 :                 netdev_err(priv->dev, "failed to configure EST\n");
     721                 :          0 :                 goto disable;
     722                 :            :         }
     723                 :            : 
     724                 :          0 :         netdev_info(priv->dev, "configured EST\n");
     725                 :          0 :         return 0;
     726                 :            : 
     727                 :          0 : disable:
     728                 :          0 :         priv->plat->est->enable = false;
     729   [ #  #  #  # ]:          0 :         stmmac_est_configure(priv, priv->ioaddr, priv->plat->est,
     730                 :            :                              priv->plat->clk_ptp_rate);
     731                 :            :         return ret;
     732                 :            : }
     733                 :            : 
     734                 :          0 : static int tc_setup_etf(struct stmmac_priv *priv,
     735                 :            :                         struct tc_etf_qopt_offload *qopt)
     736                 :            : {
     737         [ #  # ]:          0 :         if (!priv->dma_cap.tbssel)
     738                 :            :                 return -EOPNOTSUPP;
     739         [ #  # ]:          0 :         if (qopt->queue >= priv->plat->tx_queues_to_use)
     740                 :            :                 return -EINVAL;
     741         [ #  # ]:          0 :         if (!(priv->tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
     742                 :            :                 return -EINVAL;
     743                 :            : 
     744         [ #  # ]:          0 :         if (qopt->enable)
     745                 :          0 :                 priv->tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN;
     746                 :            :         else
     747                 :          0 :                 priv->tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN;
     748                 :            : 
     749                 :          0 :         netdev_info(priv->dev, "%s ETF for Queue %d\n",
     750         [ #  # ]:          0 :                     qopt->enable ? "enabled" : "disabled", qopt->queue);
     751                 :          0 :         return 0;
     752                 :            : }
     753                 :            : 
     754                 :            : const struct stmmac_tc_ops dwmac510_tc_ops = {
     755                 :            :         .init = tc_init,
     756                 :            :         .setup_cls_u32 = tc_setup_cls_u32,
     757                 :            :         .setup_cbs = tc_setup_cbs,
     758                 :            :         .setup_cls = tc_setup_cls,
     759                 :            :         .setup_taprio = tc_setup_taprio,
     760                 :            :         .setup_etf = tc_setup_etf,
     761                 :            : };

Generated by: LCOV version 1.14