Merge branch 'mlx5-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mellanox...
[sfrench/cifs-2.6.git] / drivers / net / ethernet / netronome / nfp / nfp_port.c
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
3
4 #include <linux/lockdep.h>
5 #include <linux/netdevice.h>
6 #include <net/switchdev.h>
7
8 #include "nfpcore/nfp_cpp.h"
9 #include "nfpcore/nfp_nsp.h"
10 #include "nfp_app.h"
11 #include "nfp_main.h"
12 #include "nfp_net.h"
13 #include "nfp_port.h"
14
15 struct nfp_port *nfp_port_from_netdev(struct net_device *netdev)
16 {
17         if (nfp_netdev_is_nfp_net(netdev)) {
18                 struct nfp_net *nn = netdev_priv(netdev);
19
20                 return nn->port;
21         }
22
23         if (nfp_netdev_is_nfp_repr(netdev)) {
24                 struct nfp_repr *repr = netdev_priv(netdev);
25
26                 return repr->port;
27         }
28
29         WARN(1, "Unknown netdev type for nfp_port\n");
30
31         return NULL;
32 }
33
34 static int
35 nfp_port_attr_get(struct net_device *netdev, struct switchdev_attr *attr)
36 {
37         struct nfp_port *port;
38
39         port = nfp_port_from_netdev(netdev);
40         if (!port)
41                 return -EOPNOTSUPP;
42
43         switch (attr->id) {
44         case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: {
45                 const u8 *serial;
46                 /* N.B: attr->u.ppid.id is binary data */
47                 attr->u.ppid.id_len = nfp_cpp_serial(port->app->cpp, &serial);
48                 memcpy(&attr->u.ppid.id, serial, attr->u.ppid.id_len);
49                 break;
50         }
51         default:
52                 return -EOPNOTSUPP;
53         }
54
55         return 0;
56 }
57
58 const struct switchdev_ops nfp_port_switchdev_ops = {
59         .switchdev_port_attr_get        = nfp_port_attr_get,
60 };
61
62 int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type,
63                       void *type_data)
64 {
65         struct nfp_port *port;
66
67         port = nfp_port_from_netdev(netdev);
68         if (!port)
69                 return -EOPNOTSUPP;
70
71         return nfp_app_setup_tc(port->app, netdev, type, type_data);
72 }
73
74 int nfp_port_set_features(struct net_device *netdev, netdev_features_t features)
75 {
76         struct nfp_port *port;
77
78         port = nfp_port_from_netdev(netdev);
79         if (!port)
80                 return 0;
81
82         if ((netdev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) &&
83             port->tc_offload_cnt) {
84                 netdev_err(netdev, "Cannot disable HW TC offload while offloads active\n");
85                 return -EBUSY;
86         }
87
88         return 0;
89 }
90
91 struct nfp_port *
92 nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id)
93 {
94         struct nfp_port *port;
95
96         lockdep_assert_held(&pf->lock);
97
98         if (type != NFP_PORT_PHYS_PORT)
99                 return NULL;
100
101         list_for_each_entry(port, &pf->ports, port_list)
102                 if (port->eth_id == id)
103                         return port;
104
105         return NULL;
106 }
107
108 struct nfp_eth_table_port *__nfp_port_get_eth_port(struct nfp_port *port)
109 {
110         if (!port)
111                 return NULL;
112         if (port->type != NFP_PORT_PHYS_PORT)
113                 return NULL;
114
115         return port->eth_port;
116 }
117
118 struct nfp_eth_table_port *nfp_port_get_eth_port(struct nfp_port *port)
119 {
120         if (!__nfp_port_get_eth_port(port))
121                 return NULL;
122
123         if (test_bit(NFP_PORT_CHANGED, &port->flags))
124                 if (nfp_net_refresh_eth_port(port))
125                         return NULL;
126
127         return __nfp_port_get_eth_port(port);
128 }
129
130 int
131 nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len)
132 {
133         struct nfp_eth_table_port *eth_port;
134         struct nfp_port *port;
135         int n;
136
137         port = nfp_port_from_netdev(netdev);
138         if (!port)
139                 return -EOPNOTSUPP;
140
141         switch (port->type) {
142         case NFP_PORT_PHYS_PORT:
143                 eth_port = __nfp_port_get_eth_port(port);
144                 if (!eth_port)
145                         return -EOPNOTSUPP;
146
147                 if (!eth_port->is_split)
148                         n = snprintf(name, len, "p%d", eth_port->label_port);
149                 else
150                         n = snprintf(name, len, "p%ds%d", eth_port->label_port,
151                                      eth_port->label_subport);
152                 break;
153         case NFP_PORT_PF_PORT:
154                 if (!port->pf_split)
155                         n = snprintf(name, len, "pf%d", port->pf_id);
156                 else
157                         n = snprintf(name, len, "pf%ds%d", port->pf_id,
158                                      port->pf_split_id);
159                 break;
160         case NFP_PORT_VF_PORT:
161                 n = snprintf(name, len, "pf%dvf%d", port->pf_id, port->vf_id);
162                 break;
163         default:
164                 return -EOPNOTSUPP;
165         }
166
167         if (n >= len)
168                 return -EINVAL;
169
170         return 0;
171 }
172
173 /**
174  * nfp_port_configure() - helper to set the interface configured bit
175  * @netdev:     net_device instance
176  * @configed:   Desired state
177  *
178  * Helper to set the ifup/ifdown state on the PHY only if there is a physical
179  * interface associated with the netdev.
180  *
181  * Return:
182  * 0 - configuration successful (or no change);
183  * -ERRNO - configuration failed.
184  */
185 int nfp_port_configure(struct net_device *netdev, bool configed)
186 {
187         struct nfp_eth_table_port *eth_port;
188         struct nfp_port *port;
189         int err;
190
191         port = nfp_port_from_netdev(netdev);
192         eth_port = __nfp_port_get_eth_port(port);
193         if (!eth_port)
194                 return 0;
195         if (port->eth_forced)
196                 return 0;
197
198         err = nfp_eth_set_configured(port->app->cpp, eth_port->index, configed);
199         return err < 0 && err != -EOPNOTSUPP ? err : 0;
200 }
201
202 int nfp_port_init_phy_port(struct nfp_pf *pf, struct nfp_app *app,
203                            struct nfp_port *port, unsigned int id)
204 {
205         /* Check if vNIC has external port associated and cfg is OK */
206         if (!pf->eth_tbl || id >= pf->eth_tbl->count) {
207                 nfp_err(app->cpp,
208                         "NSP port entries don't match vNICs (no entry %d)\n",
209                         id);
210                 return -EINVAL;
211         }
212         if (pf->eth_tbl->ports[id].override_changed) {
213                 nfp_warn(app->cpp,
214                          "Config changed for port #%d, reboot required before port will be operational\n",
215                          pf->eth_tbl->ports[id].index);
216                 port->type = NFP_PORT_INVALID;
217                 return 0;
218         }
219
220         port->eth_port = &pf->eth_tbl->ports[id];
221         port->eth_id = pf->eth_tbl->ports[id].index;
222         if (pf->mac_stats_mem)
223                 port->eth_stats =
224                         pf->mac_stats_mem + port->eth_id * NFP_MAC_STATS_SIZE;
225
226         return 0;
227 }
228
229 struct nfp_port *
230 nfp_port_alloc(struct nfp_app *app, enum nfp_port_type type,
231                struct net_device *netdev)
232 {
233         struct nfp_port *port;
234
235         port = kzalloc(sizeof(*port), GFP_KERNEL);
236         if (!port)
237                 return ERR_PTR(-ENOMEM);
238
239         port->netdev = netdev;
240         port->type = type;
241         port->app = app;
242
243         list_add_tail(&port->port_list, &app->pf->ports);
244
245         return port;
246 }
247
248 void nfp_port_free(struct nfp_port *port)
249 {
250         if (!port)
251                 return;
252         list_del(&port->port_list);
253         kfree(port);
254 }