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 }
255