gpio: rcar: Fix runtime PM imbalance on error
[linux/fpc-iii.git] / drivers / net / ethernet / mscc / ocelot_tc.c
blobd326e231f0ad00ac3d451d9465a020b43cadaa23
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Microsemi Ocelot Switch TC driver
4 * Copyright (c) 2019 Microsemi Corporation
5 */
7 #include <soc/mscc/ocelot.h>
8 #include "ocelot_tc.h"
9 #include "ocelot_ace.h"
10 #include <net/pkt_cls.h>
12 static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
13 struct tc_cls_matchall_offload *f,
14 bool ingress)
16 struct netlink_ext_ack *extack = f->common.extack;
17 struct ocelot *ocelot = priv->port.ocelot;
18 struct ocelot_policer pol = { 0 };
19 struct flow_action_entry *action;
20 int port = priv->chip_port;
21 int err;
23 if (!ingress) {
24 NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
25 return -EOPNOTSUPP;
28 switch (f->command) {
29 case TC_CLSMATCHALL_REPLACE:
30 if (!flow_offload_has_one_action(&f->rule->action)) {
31 NL_SET_ERR_MSG_MOD(extack,
32 "Only one action is supported");
33 return -EOPNOTSUPP;
36 if (priv->tc.block_shared) {
37 NL_SET_ERR_MSG_MOD(extack,
38 "Rate limit is not supported on shared blocks");
39 return -EOPNOTSUPP;
42 action = &f->rule->action.entries[0];
44 if (action->id != FLOW_ACTION_POLICE) {
45 NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
46 return -EOPNOTSUPP;
49 if (priv->tc.police_id && priv->tc.police_id != f->cookie) {
50 NL_SET_ERR_MSG_MOD(extack,
51 "Only one policer per port is supported\n");
52 return -EEXIST;
55 pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
56 pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
57 PSCHED_NS2TICKS(action->police.burst),
58 PSCHED_TICKS_PER_SEC);
60 err = ocelot_port_policer_add(ocelot, port, &pol);
61 if (err) {
62 NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
63 return err;
66 priv->tc.police_id = f->cookie;
67 priv->tc.offload_cnt++;
68 return 0;
69 case TC_CLSMATCHALL_DESTROY:
70 if (priv->tc.police_id != f->cookie)
71 return -ENOENT;
73 err = ocelot_port_policer_del(ocelot, port);
74 if (err) {
75 NL_SET_ERR_MSG_MOD(extack,
76 "Could not delete policer\n");
77 return err;
79 priv->tc.police_id = 0;
80 priv->tc.offload_cnt--;
81 return 0;
82 case TC_CLSMATCHALL_STATS: /* fall through */
83 default:
84 return -EOPNOTSUPP;
88 static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
89 void *type_data,
90 void *cb_priv, bool ingress)
92 struct ocelot_port_private *priv = cb_priv;
94 if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
95 return -EOPNOTSUPP;
97 switch (type) {
98 case TC_SETUP_CLSMATCHALL:
99 return ocelot_setup_tc_cls_matchall(priv, type_data, ingress);
100 case TC_SETUP_CLSFLOWER:
101 return ocelot_setup_tc_cls_flower(priv, type_data, ingress);
102 default:
103 return -EOPNOTSUPP;
107 static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
108 void *type_data,
109 void *cb_priv)
111 return ocelot_setup_tc_block_cb(type, type_data,
112 cb_priv, true);
115 static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
116 void *type_data,
117 void *cb_priv)
119 return ocelot_setup_tc_block_cb(type, type_data,
120 cb_priv, false);
123 static LIST_HEAD(ocelot_block_cb_list);
125 static int ocelot_setup_tc_block(struct ocelot_port_private *priv,
126 struct flow_block_offload *f)
128 struct flow_block_cb *block_cb;
129 flow_setup_cb_t *cb;
131 if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
132 cb = ocelot_setup_tc_block_cb_ig;
133 priv->tc.block_shared = f->block_shared;
134 } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
135 cb = ocelot_setup_tc_block_cb_eg;
136 } else {
137 return -EOPNOTSUPP;
140 f->driver_block_list = &ocelot_block_cb_list;
142 switch (f->command) {
143 case FLOW_BLOCK_BIND:
144 if (flow_block_cb_is_busy(cb, priv, &ocelot_block_cb_list))
145 return -EBUSY;
147 block_cb = flow_block_cb_alloc(cb, priv, priv, NULL);
148 if (IS_ERR(block_cb))
149 return PTR_ERR(block_cb);
151 flow_block_cb_add(block_cb, f);
152 list_add_tail(&block_cb->driver_list, f->driver_block_list);
153 return 0;
154 case FLOW_BLOCK_UNBIND:
155 block_cb = flow_block_cb_lookup(f->block, cb, priv);
156 if (!block_cb)
157 return -ENOENT;
159 flow_block_cb_remove(block_cb, f);
160 list_del(&block_cb->driver_list);
161 return 0;
162 default:
163 return -EOPNOTSUPP;
167 int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
168 void *type_data)
170 struct ocelot_port_private *priv = netdev_priv(dev);
172 switch (type) {
173 case TC_SETUP_BLOCK:
174 return ocelot_setup_tc_block(priv, type_data);
175 default:
176 return -EOPNOTSUPP;
178 return 0;