Blame view

net/dsa/slave.c 36.7 KB
91da11f87   Lennert Buytenhek   net: Distributed ...
1
2
  /*
   * net/dsa/slave.c - Slave device handling
e84665c9c   Lennert Buytenhek   dsa: add switch c...
3
   * Copyright (c) 2008-2009 Marvell Semiconductor
91da11f87   Lennert Buytenhek   net: Distributed ...
4
5
6
7
8
9
10
11
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   */
  
  #include <linux/list.h>
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
12
  #include <linux/etherdevice.h>
b73adef67   Florian Fainelli   net: dsa: integra...
13
  #include <linux/netdevice.h>
91da11f87   Lennert Buytenhek   net: Distributed ...
14
  #include <linux/phy.h>
a28205437   Florian Fainelli   net: dsa: add inc...
15
  #include <linux/phy_fixed.h>
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
16
17
  #include <linux/of_net.h>
  #include <linux/of_mdio.h>
7f854420f   Andrew Lunn   phy: Add API for ...
18
  #include <linux/mdio.h>
f50f21274   Florian Fainelli   net: dsa: Add plu...
19
  #include <linux/list.h>
b73adef67   Florian Fainelli   net: dsa: integra...
20
  #include <net/rtnetlink.h>
f50f21274   Florian Fainelli   net: dsa: Add plu...
21
22
  #include <net/pkt_cls.h>
  #include <net/tc_act/tc_mirred.h>
b73adef67   Florian Fainelli   net: dsa: integra...
23
  #include <linux/if_bridge.h>
04ff53f96   Florian Fainelli   net: dsa: Add net...
24
  #include <linux/netpoll.h>
ea5dd34be   Vivien Didelot   net: dsa: include...
25

91da11f87   Lennert Buytenhek   net: Distributed ...
26
  #include "dsa_priv.h"
f50f21274   Florian Fainelli   net: dsa: Add plu...
27
  static bool dsa_slave_dev_check(struct net_device *dev);
91da11f87   Lennert Buytenhek   net: Distributed ...
28
29
30
31
  /* slave mii_bus handling ***************************************************/
  static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
  {
  	struct dsa_switch *ds = bus->priv;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
32
  	if (ds->phys_mii_mask & (1 << addr))
9d490b4ee   Vivien Didelot   net: dsa: rename ...
33
  		return ds->ops->phy_read(ds, addr, reg);
91da11f87   Lennert Buytenhek   net: Distributed ...
34
35
36
37
38
39
40
  
  	return 0xffff;
  }
  
  static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
  {
  	struct dsa_switch *ds = bus->priv;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
41
  	if (ds->phys_mii_mask & (1 << addr))
9d490b4ee   Vivien Didelot   net: dsa: rename ...
42
  		return ds->ops->phy_write(ds, addr, reg, val);
91da11f87   Lennert Buytenhek   net: Distributed ...
43
44
45
46
47
48
49
50
51
52
  
  	return 0;
  }
  
  void dsa_slave_mii_bus_init(struct dsa_switch *ds)
  {
  	ds->slave_mii_bus->priv = (void *)ds;
  	ds->slave_mii_bus->name = "dsa slave smi";
  	ds->slave_mii_bus->read = dsa_slave_phy_read;
  	ds->slave_mii_bus->write = dsa_slave_phy_write;
0b7b498d4   Florian Fainelli   net: dsa: Provide...
53
54
  	snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d",
  		 ds->dst->tree, ds->index);
c33063d6a   Andrew Lunn   dsa: Remove maste...
55
  	ds->slave_mii_bus->parent = ds->dev;
24df8986f   Vivien Didelot   net: dsa: set sla...
56
  	ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
91da11f87   Lennert Buytenhek   net: Distributed ...
57
58
59
60
  }
  
  
  /* slave device handling ****************************************************/
abd2be00d   Nicolas Dichtel   dsa: implement nd...
61
  static int dsa_slave_get_iflink(const struct net_device *dev)
c08408015   Lennert Buytenhek   dsa: set ->iflink...
62
63
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
c08408015   Lennert Buytenhek   dsa: set ->iflink...
64

6d3c8c0dd   Florian Fainelli   net: dsa: Remove ...
65
  	return dsa_master_netdev(p)->ifindex;
c08408015   Lennert Buytenhek   dsa: set ->iflink...
66
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
67
68
  static int dsa_slave_open(struct net_device *dev)
  {
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
69
  	struct dsa_slave_priv *p = netdev_priv(dev);
f3d736c47   Vivien Didelot   net: dsa: remove ...
70
71
  	struct dsa_port *dp = p->dp;
  	struct dsa_switch *ds = dp->ds;
6d3c8c0dd   Florian Fainelli   net: dsa: Remove ...
72
  	struct net_device *master = dsa_master_netdev(p);
f3d736c47   Vivien Didelot   net: dsa: remove ...
73
  	u8 stp_state = dp->bridge_dev ? BR_STATE_BLOCKING : BR_STATE_FORWARDING;
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
74
75
76
77
  	int err;
  
  	if (!(master->flags & IFF_UP))
  		return -ENETDOWN;
8feedbb4a   Joe Perches   dsa: Convert comp...
78
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) {
a748ee242   Jiri Pirko   net: move address...
79
  		err = dev_uc_add(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  		if (err < 0)
  			goto out;
  	}
  
  	if (dev->flags & IFF_ALLMULTI) {
  		err = dev_set_allmulti(master, 1);
  		if (err < 0)
  			goto del_unicast;
  	}
  	if (dev->flags & IFF_PROMISC) {
  		err = dev_set_promiscuity(master, 1);
  		if (err < 0)
  			goto clear_allmulti;
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
94
  	if (ds->ops->port_enable) {
afdcf151c   Vivien Didelot   net: dsa: store a...
95
  		err = ds->ops->port_enable(ds, p->dp->index, p->phy);
b2f2af21e   Florian Fainelli   net: dsa: allow e...
96
97
98
  		if (err)
  			goto clear_promisc;
  	}
fd3645413   Vivien Didelot   net: dsa: change ...
99
  	dsa_port_set_state_now(p->dp, stp_state);
b73adef67   Florian Fainelli   net: dsa: integra...
100

f7f1de51e   Florian Fainelli   net: dsa: start a...
101
102
  	if (p->phy)
  		phy_start(p->phy);
91da11f87   Lennert Buytenhek   net: Distributed ...
103
  	return 0;
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
104

b2f2af21e   Florian Fainelli   net: dsa: allow e...
105
106
  clear_promisc:
  	if (dev->flags & IFF_PROMISC)
4fdeddfe0   Gilad Ben-Yossef   dsa: fix promiscu...
107
  		dev_set_promiscuity(master, -1);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
108
109
110
111
  clear_allmulti:
  	if (dev->flags & IFF_ALLMULTI)
  		dev_set_allmulti(master, -1);
  del_unicast:
8feedbb4a   Joe Perches   dsa: Convert comp...
112
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
a748ee242   Jiri Pirko   net: move address...
113
  		dev_uc_del(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
114
115
  out:
  	return err;
91da11f87   Lennert Buytenhek   net: Distributed ...
116
117
118
119
  }
  
  static int dsa_slave_close(struct net_device *dev)
  {
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
120
  	struct dsa_slave_priv *p = netdev_priv(dev);
6d3c8c0dd   Florian Fainelli   net: dsa: Remove ...
121
  	struct net_device *master = dsa_master_netdev(p);
afdcf151c   Vivien Didelot   net: dsa: store a...
122
  	struct dsa_switch *ds = p->dp->ds;
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
123

f7f1de51e   Florian Fainelli   net: dsa: start a...
124
125
  	if (p->phy)
  		phy_stop(p->phy);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
126
  	dev_mc_unsync(master, dev);
a748ee242   Jiri Pirko   net: move address...
127
  	dev_uc_unsync(master, dev);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
128
129
130
131
  	if (dev->flags & IFF_ALLMULTI)
  		dev_set_allmulti(master, -1);
  	if (dev->flags & IFF_PROMISC)
  		dev_set_promiscuity(master, -1);
8feedbb4a   Joe Perches   dsa: Convert comp...
132
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
a748ee242   Jiri Pirko   net: move address...
133
  		dev_uc_del(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
134

9d490b4ee   Vivien Didelot   net: dsa: rename ...
135
  	if (ds->ops->port_disable)
afdcf151c   Vivien Didelot   net: dsa: store a...
136
  		ds->ops->port_disable(ds, p->dp->index, p->phy);
b2f2af21e   Florian Fainelli   net: dsa: allow e...
137

fd3645413   Vivien Didelot   net: dsa: change ...
138
  	dsa_port_set_state_now(p->dp, BR_STATE_DISABLED);
b73adef67   Florian Fainelli   net: dsa: integra...
139

91da11f87   Lennert Buytenhek   net: Distributed ...
140
141
142
143
144
145
  	return 0;
  }
  
  static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
6d3c8c0dd   Florian Fainelli   net: dsa: Remove ...
146
  	struct net_device *master = dsa_master_netdev(p);
91da11f87   Lennert Buytenhek   net: Distributed ...
147
148
149
150
151
152
153
154
155
156
  
  	if (change & IFF_ALLMULTI)
  		dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
  	if (change & IFF_PROMISC)
  		dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1);
  }
  
  static void dsa_slave_set_rx_mode(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
6d3c8c0dd   Florian Fainelli   net: dsa: Remove ...
157
  	struct net_device *master = dsa_master_netdev(p);
91da11f87   Lennert Buytenhek   net: Distributed ...
158
159
  
  	dev_mc_sync(master, dev);
a748ee242   Jiri Pirko   net: move address...
160
  	dev_uc_sync(master, dev);
91da11f87   Lennert Buytenhek   net: Distributed ...
161
  }
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
162
  static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
91da11f87   Lennert Buytenhek   net: Distributed ...
163
  {
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
164
  	struct dsa_slave_priv *p = netdev_priv(dev);
6d3c8c0dd   Florian Fainelli   net: dsa: Remove ...
165
  	struct net_device *master = dsa_master_netdev(p);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
166
167
168
169
170
171
172
173
  	struct sockaddr *addr = a;
  	int err;
  
  	if (!is_valid_ether_addr(addr->sa_data))
  		return -EADDRNOTAVAIL;
  
  	if (!(dev->flags & IFF_UP))
  		goto out;
8feedbb4a   Joe Perches   dsa: Convert comp...
174
  	if (!ether_addr_equal(addr->sa_data, master->dev_addr)) {
a748ee242   Jiri Pirko   net: move address...
175
  		err = dev_uc_add(master, addr->sa_data);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
176
177
178
  		if (err < 0)
  			return err;
  	}
8feedbb4a   Joe Perches   dsa: Convert comp...
179
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
a748ee242   Jiri Pirko   net: move address...
180
  		dev_uc_del(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
181
182
  
  out:
d08f161a1   Joe Perches   dsa: Use ether_ad...
183
  	ether_addr_copy(dev->dev_addr, addr->sa_data);
91da11f87   Lennert Buytenhek   net: Distributed ...
184
185
186
  
  	return 0;
  }
2bedde1ab   Arkadi Sharshevsky   net: dsa: Move FD...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  struct dsa_slave_dump_ctx {
  	struct net_device *dev;
  	struct sk_buff *skb;
  	struct netlink_callback *cb;
  	int idx;
  };
  
  static int
  dsa_slave_port_fdb_do_dump(const unsigned char *addr, u16 vid,
  			   bool is_static, void *data)
  {
  	struct dsa_slave_dump_ctx *dump = data;
  	u32 portid = NETLINK_CB(dump->cb->skb).portid;
  	u32 seq = dump->cb->nlh->nlmsg_seq;
  	struct nlmsghdr *nlh;
  	struct ndmsg *ndm;
  
  	if (dump->idx < dump->cb->args[2])
  		goto skip;
  
  	nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
  			sizeof(*ndm), NLM_F_MULTI);
  	if (!nlh)
  		return -EMSGSIZE;
  
  	ndm = nlmsg_data(nlh);
  	ndm->ndm_family  = AF_BRIDGE;
  	ndm->ndm_pad1    = 0;
  	ndm->ndm_pad2    = 0;
  	ndm->ndm_flags   = NTF_SELF;
  	ndm->ndm_type    = 0;
  	ndm->ndm_ifindex = dump->dev->ifindex;
  	ndm->ndm_state   = is_static ? NUD_NOARP : NUD_REACHABLE;
  
  	if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
  		goto nla_put_failure;
  
  	if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
  		goto nla_put_failure;
  
  	nlmsg_end(dump->skb, nlh);
  
  skip:
  	dump->idx++;
  	return 0;
  
  nla_put_failure:
  	nlmsg_cancel(dump->skb, nlh);
  	return -EMSGSIZE;
  }
  
  static int
  dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
  		   struct net_device *dev, struct net_device *filter_dev,
  		   int *idx)
  {
  	struct dsa_slave_dump_ctx dump = {
  		.dev = dev,
  		.skb = skb,
  		.cb = cb,
  		.idx = *idx,
  	};
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_port *dp = p->dp;
  	struct dsa_switch *ds = dp->ds;
  	int err;
  
  	if (!ds->ops->port_fdb_dump)
  		return -EOPNOTSUPP;
  
  	err = ds->ops->port_fdb_dump(ds, dp->index,
  				     dsa_slave_port_fdb_do_dump,
  				     &dump);
  	*idx = dump.idx;
  	return err;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
263
264
265
  static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
91da11f87   Lennert Buytenhek   net: Distributed ...
266
267
  
  	if (p->phy != NULL)
28b041139   Richard Cochran   net: preserve ifr...
268
  		return phy_mii_ioctl(p->phy, ifr, cmd);
91da11f87   Lennert Buytenhek   net: Distributed ...
269
270
271
  
  	return -EOPNOTSUPP;
  }
356360625   Scott Feldman   switchdev: conver...
272
  static int dsa_slave_port_attr_set(struct net_device *dev,
f7fadf304   Jiri Pirko   switchdev: make s...
273
  				   const struct switchdev_attr *attr,
7ea6eb3f5   Jiri Pirko   switchdev: introd...
274
  				   struct switchdev_trans *trans)
356360625   Scott Feldman   switchdev: conver...
275
  {
fd3645413   Vivien Didelot   net: dsa: change ...
276
277
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_port *dp = p->dp;
b8d866ac6   Vivien Didelot   net: dsa: fix pre...
278
  	int ret;
356360625   Scott Feldman   switchdev: conver...
279
280
  
  	switch (attr->id) {
1f8683987   Jiri Pirko   switchdev: rename...
281
  	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
fd3645413   Vivien Didelot   net: dsa: change ...
282
  		ret = dsa_port_set_state(dp, attr->u.stp_state, trans);
356360625   Scott Feldman   switchdev: conver...
283
  		break;
fb2dabad6   Vivien Didelot   net: dsa: support...
284
  	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
c02c4175c   Vivien Didelot   net: dsa: change ...
285
286
  		ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering,
  					      trans);
fb2dabad6   Vivien Didelot   net: dsa: support...
287
  		break;
34a79f63b   Vivien Didelot   net: dsa: support...
288
  	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
072bb1903   Vivien Didelot   net: dsa: change ...
289
  		ret = dsa_port_ageing_time(dp, attr->u.ageing_time, trans);
34a79f63b   Vivien Didelot   net: dsa: support...
290
  		break;
356360625   Scott Feldman   switchdev: conver...
291
292
293
294
295
296
297
  	default:
  		ret = -EOPNOTSUPP;
  		break;
  	}
  
  	return ret;
  }
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
298
  static int dsa_slave_port_obj_add(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
299
  				  const struct switchdev_obj *obj,
7ea6eb3f5   Jiri Pirko   switchdev: introd...
300
  				  struct switchdev_trans *trans)
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
301
  {
3fdb023b5   Vivien Didelot   net: dsa: change ...
302
303
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_port *dp = p->dp;
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
304
305
306
307
308
309
  	int err;
  
  	/* For the prepare phase, ensure the full set of changes is feasable in
  	 * one go in order to signal a failure properly. If an operation is not
  	 * supported, return -EOPNOTSUPP.
  	 */
9e8f4a548   Jiri Pirko   switchdev: push o...
310
  	switch (obj->id) {
8df302552   Vivien Didelot   net: dsa: add MDB...
311
  	case SWITCHDEV_OBJ_ID_PORT_MDB:
bcebb976e   Vivien Didelot   net: dsa: change ...
312
  		err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans);
8df302552   Vivien Didelot   net: dsa: add MDB...
313
  		break;
57d80838d   Jiri Pirko   switchdev: rename...
314
  	case SWITCHDEV_OBJ_ID_PORT_VLAN:
01676d129   Vivien Didelot   net: dsa: change ...
315
316
  		err = dsa_port_vlan_add(dp, SWITCHDEV_OBJ_PORT_VLAN(obj),
  					trans);
111495361   Vivien Didelot   net: dsa: add sup...
317
  		break;
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
318
319
320
321
322
323
324
325
326
  	default:
  		err = -EOPNOTSUPP;
  		break;
  	}
  
  	return err;
  }
  
  static int dsa_slave_port_obj_del(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
327
  				  const struct switchdev_obj *obj)
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
328
  {
3fdb023b5   Vivien Didelot   net: dsa: change ...
329
330
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_port *dp = p->dp;
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
331
  	int err;
9e8f4a548   Jiri Pirko   switchdev: push o...
332
  	switch (obj->id) {
8df302552   Vivien Didelot   net: dsa: add MDB...
333
  	case SWITCHDEV_OBJ_ID_PORT_MDB:
bcebb976e   Vivien Didelot   net: dsa: change ...
334
  		err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
8df302552   Vivien Didelot   net: dsa: add MDB...
335
  		break;
57d80838d   Jiri Pirko   switchdev: rename...
336
  	case SWITCHDEV_OBJ_ID_PORT_VLAN:
01676d129   Vivien Didelot   net: dsa: change ...
337
  		err = dsa_port_vlan_del(dp, SWITCHDEV_OBJ_PORT_VLAN(obj));
111495361   Vivien Didelot   net: dsa: add sup...
338
  		break;
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
339
340
341
342
343
344
345
  	default:
  		err = -EOPNOTSUPP;
  		break;
  	}
  
  	return err;
  }
f8e20a9f8   Scott Feldman   switchdev: conver...
346
347
  static int dsa_slave_port_attr_get(struct net_device *dev,
  				   struct switchdev_attr *attr)
b73adef67   Florian Fainelli   net: dsa: integra...
348
349
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
350
  	struct dsa_switch *ds = p->dp->ds;
b73adef67   Florian Fainelli   net: dsa: integra...
351

f8e20a9f8   Scott Feldman   switchdev: conver...
352
  	switch (attr->id) {
1f8683987   Jiri Pirko   switchdev: rename...
353
  	case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
42275bd8f   Scott Feldman   switchdev: don't ...
354
355
  		attr->u.ppid.id_len = sizeof(ds->index);
  		memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len);
f8e20a9f8   Scott Feldman   switchdev: conver...
356
  		break;
c9e2105e2   Arkadi Sharshevsky   net: dsa: Add sup...
357
358
359
  	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
  		attr->u.brport_flags_support = 0;
  		break;
f8e20a9f8   Scott Feldman   switchdev: conver...
360
361
362
  	default:
  		return -EOPNOTSUPP;
  	}
b73adef67   Florian Fainelli   net: dsa: integra...
363
364
365
  
  	return 0;
  }
04ff53f96   Florian Fainelli   net: dsa: Add net...
366
367
368
369
370
371
372
373
374
375
376
  static inline netdev_tx_t dsa_netpoll_send_skb(struct dsa_slave_priv *p,
  					       struct sk_buff *skb)
  {
  #ifdef CONFIG_NET_POLL_CONTROLLER
  	if (p->netpoll)
  		netpoll_send_skb(p->netpoll, skb);
  #else
  	BUG();
  #endif
  	return NETDEV_TX_OK;
  }
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
377
378
379
  static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
380
  	struct pcpu_sw_netstats *s;
4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
381
  	struct sk_buff *nskb;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
382

5f6b4e14c   Florian Fainelli   net: dsa: User pe...
383
384
385
386
387
  	s = this_cpu_ptr(p->stats64);
  	u64_stats_update_begin(&s->syncp);
  	s->tx_packets++;
  	s->tx_bytes += skb->len;
  	u64_stats_update_end(&s->syncp);
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
388

fe47d5630   Vivien Didelot   net: dsa: factor ...
389
390
391
  	/* Transmit function may have to reallocate the original SKB,
  	 * in which case it must have freed it. Only free it here on error.
  	 */
4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
392
  	nskb = p->xmit(skb, dev);
fe47d5630   Vivien Didelot   net: dsa: factor ...
393
394
  	if (!nskb) {
  		kfree_skb(skb);
4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
395
  		return NETDEV_TX_OK;
fe47d5630   Vivien Didelot   net: dsa: factor ...
396
  	}
5aed85cec   Florian Fainelli   net: dsa: allow s...
397

04ff53f96   Florian Fainelli   net: dsa: Add net...
398
399
400
401
402
  	/* SKB for netpoll still need to be mangled with the protocol-specific
  	 * tag to be successfully transmitted
  	 */
  	if (unlikely(netpoll_tx_running(dev)))
  		return dsa_netpoll_send_skb(p, nskb);
4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
403
404
405
  	/* Queue the SKB for transmission on the parent interface, but
  	 * do not modify its EtherType
  	 */
6d3c8c0dd   Florian Fainelli   net: dsa: Remove ...
406
  	nskb->dev = dsa_master_netdev(p);
4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
407
  	dev_queue_xmit(nskb);
5aed85cec   Florian Fainelli   net: dsa: allow s...
408
409
410
  
  	return NETDEV_TX_OK;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
411
412
  /* ethtool operations *******************************************************/
  static int
bb10bb3ea   Philippe Reynes   net: dsa: slave: ...
413
414
  dsa_slave_get_link_ksettings(struct net_device *dev,
  			     struct ethtool_link_ksettings *cmd)
91da11f87   Lennert Buytenhek   net: Distributed ...
415
416
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
91da11f87   Lennert Buytenhek   net: Distributed ...
417

5514174fe   yuval.shaia@oracle.com   net: phy: Make ph...
418
419
  	if (!p->phy)
  		return -EOPNOTSUPP;
91da11f87   Lennert Buytenhek   net: Distributed ...
420

5514174fe   yuval.shaia@oracle.com   net: phy: Make ph...
421
422
423
  	phy_ethtool_ksettings_get(p->phy, cmd);
  
  	return 0;
91da11f87   Lennert Buytenhek   net: Distributed ...
424
425
426
  }
  
  static int
bb10bb3ea   Philippe Reynes   net: dsa: slave: ...
427
428
  dsa_slave_set_link_ksettings(struct net_device *dev,
  			     const struct ethtool_link_ksettings *cmd)
91da11f87   Lennert Buytenhek   net: Distributed ...
429
430
431
432
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  
  	if (p->phy != NULL)
bb10bb3ea   Philippe Reynes   net: dsa: slave: ...
433
  		return phy_ethtool_ksettings_set(p->phy, cmd);
91da11f87   Lennert Buytenhek   net: Distributed ...
434
435
436
437
438
439
440
  
  	return -EOPNOTSUPP;
  }
  
  static void dsa_slave_get_drvinfo(struct net_device *dev,
  				  struct ethtool_drvinfo *drvinfo)
  {
7826d43f2   Jiri Pirko   ethtool: fix drvi...
441
  	strlcpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver));
7826d43f2   Jiri Pirko   ethtool: fix drvi...
442
443
  	strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
  	strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
91da11f87   Lennert Buytenhek   net: Distributed ...
444
  }
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
445
446
447
  static int dsa_slave_get_regs_len(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
448
  	struct dsa_switch *ds = p->dp->ds;
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
449

9d490b4ee   Vivien Didelot   net: dsa: rename ...
450
  	if (ds->ops->get_regs_len)
afdcf151c   Vivien Didelot   net: dsa: store a...
451
  		return ds->ops->get_regs_len(ds, p->dp->index);
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
452
453
454
455
456
457
458
459
  
  	return -EOPNOTSUPP;
  }
  
  static void
  dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
460
  	struct dsa_switch *ds = p->dp->ds;
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
461

9d490b4ee   Vivien Didelot   net: dsa: rename ...
462
  	if (ds->ops->get_regs)
afdcf151c   Vivien Didelot   net: dsa: store a...
463
  		ds->ops->get_regs(ds, p->dp->index, regs, _p);
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
464
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
  static int dsa_slave_nway_reset(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  
  	if (p->phy != NULL)
  		return genphy_restart_aneg(p->phy);
  
  	return -EOPNOTSUPP;
  }
  
  static u32 dsa_slave_get_link(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  
  	if (p->phy != NULL) {
  		genphy_update_link(p->phy);
  		return p->phy->link;
  	}
  
  	return -EOPNOTSUPP;
  }
6793abb4e   Guenter Roeck   net: dsa: Add sup...
486
487
488
  static int dsa_slave_get_eeprom_len(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
489
  	struct dsa_switch *ds = p->dp->ds;
6793abb4e   Guenter Roeck   net: dsa: Add sup...
490

0e5760440   Andrew Lunn   net: dsa: slave: ...
491
  	if (ds->cd && ds->cd->eeprom_len)
ff04955c2   Andrew Lunn   dsa: Rename switc...
492
  		return ds->cd->eeprom_len;
6793abb4e   Guenter Roeck   net: dsa: Add sup...
493

9d490b4ee   Vivien Didelot   net: dsa: rename ...
494
495
  	if (ds->ops->get_eeprom_len)
  		return ds->ops->get_eeprom_len(ds);
6793abb4e   Guenter Roeck   net: dsa: Add sup...
496
497
498
499
500
501
502
503
  
  	return 0;
  }
  
  static int dsa_slave_get_eeprom(struct net_device *dev,
  				struct ethtool_eeprom *eeprom, u8 *data)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
504
  	struct dsa_switch *ds = p->dp->ds;
6793abb4e   Guenter Roeck   net: dsa: Add sup...
505

9d490b4ee   Vivien Didelot   net: dsa: rename ...
506
507
  	if (ds->ops->get_eeprom)
  		return ds->ops->get_eeprom(ds, eeprom, data);
6793abb4e   Guenter Roeck   net: dsa: Add sup...
508
509
510
511
512
513
514
515
  
  	return -EOPNOTSUPP;
  }
  
  static int dsa_slave_set_eeprom(struct net_device *dev,
  				struct ethtool_eeprom *eeprom, u8 *data)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
516
  	struct dsa_switch *ds = p->dp->ds;
6793abb4e   Guenter Roeck   net: dsa: Add sup...
517

9d490b4ee   Vivien Didelot   net: dsa: rename ...
518
519
  	if (ds->ops->set_eeprom)
  		return ds->ops->set_eeprom(ds, eeprom, data);
6793abb4e   Guenter Roeck   net: dsa: Add sup...
520
521
522
  
  	return -EOPNOTSUPP;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
523
524
525
526
  static void dsa_slave_get_strings(struct net_device *dev,
  				  uint32_t stringset, uint8_t *data)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
527
  	struct dsa_switch *ds = p->dp->ds;
91da11f87   Lennert Buytenhek   net: Distributed ...
528
529
530
531
532
533
534
535
  
  	if (stringset == ETH_SS_STATS) {
  		int len = ETH_GSTRING_LEN;
  
  		strncpy(data, "tx_packets", len);
  		strncpy(data + len, "tx_bytes", len);
  		strncpy(data + 2 * len, "rx_packets", len);
  		strncpy(data + 3 * len, "rx_bytes", len);
9d490b4ee   Vivien Didelot   net: dsa: rename ...
536
  		if (ds->ops->get_strings)
afdcf151c   Vivien Didelot   net: dsa: store a...
537
  			ds->ops->get_strings(ds, p->dp->index, data + 4 * len);
91da11f87   Lennert Buytenhek   net: Distributed ...
538
539
  	}
  }
badf3ada6   Florian Fainelli   net: dsa: Provide...
540
541
542
543
544
  static void dsa_cpu_port_get_ethtool_stats(struct net_device *dev,
  					   struct ethtool_stats *stats,
  					   uint64_t *data)
  {
  	struct dsa_switch_tree *dst = dev->dsa_ptr;
3cc9f2573   Florian Fainelli   net: dsa: Introdu...
545
546
547
  	struct dsa_port *cpu_dp = dsa_get_cpu_port(dst);
  	struct dsa_switch *ds = cpu_dp->ds;
  	s8 cpu_port = cpu_dp->index;
badf3ada6   Florian Fainelli   net: dsa: Provide...
548
  	int count = 0;
3cc9f2573   Florian Fainelli   net: dsa: Introdu...
549
550
551
  	if (cpu_dp->ethtool_ops.get_sset_count) {
  		count = cpu_dp->ethtool_ops.get_sset_count(dev, ETH_SS_STATS);
  		cpu_dp->ethtool_ops.get_ethtool_stats(dev, stats, data);
badf3ada6   Florian Fainelli   net: dsa: Provide...
552
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
553
554
  	if (ds->ops->get_ethtool_stats)
  		ds->ops->get_ethtool_stats(ds, cpu_port, data + count);
badf3ada6   Florian Fainelli   net: dsa: Provide...
555
556
557
558
559
  }
  
  static int dsa_cpu_port_get_sset_count(struct net_device *dev, int sset)
  {
  	struct dsa_switch_tree *dst = dev->dsa_ptr;
3cc9f2573   Florian Fainelli   net: dsa: Introdu...
560
561
  	struct dsa_port *cpu_dp = dsa_get_cpu_port(dst);
  	struct dsa_switch *ds = cpu_dp->ds;
badf3ada6   Florian Fainelli   net: dsa: Provide...
562
  	int count = 0;
3cc9f2573   Florian Fainelli   net: dsa: Introdu...
563
564
  	if (cpu_dp->ethtool_ops.get_sset_count)
  		count += cpu_dp->ethtool_ops.get_sset_count(dev, sset);
badf3ada6   Florian Fainelli   net: dsa: Provide...
565

9d490b4ee   Vivien Didelot   net: dsa: rename ...
566
567
  	if (sset == ETH_SS_STATS && ds->ops->get_sset_count)
  		count += ds->ops->get_sset_count(ds);
badf3ada6   Florian Fainelli   net: dsa: Provide...
568
569
570
571
572
573
574
575
  
  	return count;
  }
  
  static void dsa_cpu_port_get_strings(struct net_device *dev,
  				     uint32_t stringset, uint8_t *data)
  {
  	struct dsa_switch_tree *dst = dev->dsa_ptr;
3cc9f2573   Florian Fainelli   net: dsa: Introdu...
576
577
578
  	struct dsa_port *cpu_dp = dsa_get_cpu_port(dst);
  	struct dsa_switch *ds = cpu_dp->ds;
  	s8 cpu_port = cpu_dp->index;
badf3ada6   Florian Fainelli   net: dsa: Provide...
579
580
581
582
583
584
585
586
587
  	int len = ETH_GSTRING_LEN;
  	int mcount = 0, count;
  	unsigned int i;
  	uint8_t pfx[4];
  	uint8_t *ndata;
  
  	snprintf(pfx, sizeof(pfx), "p%.2d", cpu_port);
  	/* We do not want to be NULL-terminated, since this is a prefix */
  	pfx[sizeof(pfx) - 1] = '_';
3cc9f2573   Florian Fainelli   net: dsa: Introdu...
588
589
590
  	if (cpu_dp->ethtool_ops.get_sset_count) {
  		mcount = cpu_dp->ethtool_ops.get_sset_count(dev, ETH_SS_STATS);
  		cpu_dp->ethtool_ops.get_strings(dev, stringset, data);
badf3ada6   Florian Fainelli   net: dsa: Provide...
591
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
592
  	if (stringset == ETH_SS_STATS && ds->ops->get_strings) {
badf3ada6   Florian Fainelli   net: dsa: Provide...
593
594
595
596
597
  		ndata = data + mcount * len;
  		/* This function copies ETH_GSTRINGS_LEN bytes, we will mangle
  		 * the output after to prepend our CPU port prefix we
  		 * constructed earlier
  		 */
9d490b4ee   Vivien Didelot   net: dsa: rename ...
598
599
  		ds->ops->get_strings(ds, cpu_port, ndata);
  		count = ds->ops->get_sset_count(ds);
badf3ada6   Florian Fainelli   net: dsa: Provide...
600
601
602
603
604
605
606
  		for (i = 0; i < count; i++) {
  			memmove(ndata + (i * len + sizeof(pfx)),
  				ndata + i * len, len - sizeof(pfx));
  			memcpy(ndata + i * len, pfx, sizeof(pfx));
  		}
  	}
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
607
608
609
610
611
  static void dsa_slave_get_ethtool_stats(struct net_device *dev,
  					struct ethtool_stats *stats,
  					uint64_t *data)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
612
  	struct dsa_switch *ds = p->dp->ds;
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
613
  	struct pcpu_sw_netstats *s;
f613ed665   Florian Fainelli   net: dsa: Add sup...
614
  	unsigned int start;
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
  	int i;
  
  	for_each_possible_cpu(i) {
  		u64 tx_packets, tx_bytes, rx_packets, rx_bytes;
  
  		s = per_cpu_ptr(p->stats64, i);
  		do {
  			start = u64_stats_fetch_begin_irq(&s->syncp);
  			tx_packets = s->tx_packets;
  			tx_bytes = s->tx_bytes;
  			rx_packets = s->rx_packets;
  			rx_bytes = s->rx_bytes;
  		} while (u64_stats_fetch_retry_irq(&s->syncp, start));
  		data[0] += tx_packets;
  		data[1] += tx_bytes;
  		data[2] += rx_packets;
  		data[3] += rx_bytes;
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
633
  	if (ds->ops->get_ethtool_stats)
afdcf151c   Vivien Didelot   net: dsa: store a...
634
  		ds->ops->get_ethtool_stats(ds, p->dp->index, data + 4);
91da11f87   Lennert Buytenhek   net: Distributed ...
635
636
637
638
639
  }
  
  static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
640
  	struct dsa_switch *ds = p->dp->ds;
91da11f87   Lennert Buytenhek   net: Distributed ...
641
642
643
644
645
  
  	if (sset == ETH_SS_STATS) {
  		int count;
  
  		count = 4;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
646
647
  		if (ds->ops->get_sset_count)
  			count += ds->ops->get_sset_count(ds);
91da11f87   Lennert Buytenhek   net: Distributed ...
648
649
650
651
652
653
  
  		return count;
  	}
  
  	return -EOPNOTSUPP;
  }
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
654
655
656
  static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
657
  	struct dsa_switch *ds = p->dp->ds;
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
658

9d490b4ee   Vivien Didelot   net: dsa: rename ...
659
  	if (ds->ops->get_wol)
afdcf151c   Vivien Didelot   net: dsa: store a...
660
  		ds->ops->get_wol(ds, p->dp->index, w);
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
661
662
663
664
665
  }
  
  static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
666
  	struct dsa_switch *ds = p->dp->ds;
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
667
  	int ret = -EOPNOTSUPP;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
668
  	if (ds->ops->set_wol)
afdcf151c   Vivien Didelot   net: dsa: store a...
669
  		ret = ds->ops->set_wol(ds, p->dp->index, w);
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
670
671
672
  
  	return ret;
  }
7905288f0   Florian Fainelli   net: dsa: allow s...
673
674
675
  static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
676
  	struct dsa_switch *ds = p->dp->ds;
7905288f0   Florian Fainelli   net: dsa: allow s...
677
  	int ret;
7b9cc7384   Vivien Didelot   net: dsa: PHY dev...
678
679
680
  	/* Port's PHY and MAC both need to be EEE capable */
  	if (!p->phy)
  		return -ENODEV;
08f500610   Vivien Didelot   net: dsa: rename ...
681
  	if (!ds->ops->set_mac_eee)
7905288f0   Florian Fainelli   net: dsa: allow s...
682
  		return -EOPNOTSUPP;
08f500610   Vivien Didelot   net: dsa: rename ...
683
  	ret = ds->ops->set_mac_eee(ds, p->dp->index, e);
7905288f0   Florian Fainelli   net: dsa: allow s...
684
685
  	if (ret)
  		return ret;
c48f7eb30   Vivien Didelot   net: dsa: call ph...
686
687
688
689
690
  	if (e->eee_enabled) {
  		ret = phy_init_eee(p->phy, 0);
  		if (ret)
  			return ret;
  	}
7b9cc7384   Vivien Didelot   net: dsa: PHY dev...
691
  	return phy_ethtool_set_eee(p->phy, e);
7905288f0   Florian Fainelli   net: dsa: allow s...
692
693
694
695
696
  }
  
  static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
697
  	struct dsa_switch *ds = p->dp->ds;
7905288f0   Florian Fainelli   net: dsa: allow s...
698
  	int ret;
7b9cc7384   Vivien Didelot   net: dsa: PHY dev...
699
700
701
  	/* Port's PHY and MAC both need to be EEE capable */
  	if (!p->phy)
  		return -ENODEV;
08f500610   Vivien Didelot   net: dsa: rename ...
702
  	if (!ds->ops->get_mac_eee)
7905288f0   Florian Fainelli   net: dsa: allow s...
703
  		return -EOPNOTSUPP;
08f500610   Vivien Didelot   net: dsa: rename ...
704
  	ret = ds->ops->get_mac_eee(ds, p->dp->index, e);
7905288f0   Florian Fainelli   net: dsa: allow s...
705
706
  	if (ret)
  		return ret;
7b9cc7384   Vivien Didelot   net: dsa: PHY dev...
707
  	return phy_ethtool_get_eee(p->phy, e);
7905288f0   Florian Fainelli   net: dsa: allow s...
708
  }
04ff53f96   Florian Fainelli   net: dsa: Add net...
709
710
711
712
713
  #ifdef CONFIG_NET_POLL_CONTROLLER
  static int dsa_slave_netpoll_setup(struct net_device *dev,
  				   struct netpoll_info *ni)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
6d3c8c0dd   Florian Fainelli   net: dsa: Remove ...
714
  	struct net_device *master = dsa_master_netdev(p);
04ff53f96   Florian Fainelli   net: dsa: Add net...
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
  	struct netpoll *netpoll;
  	int err = 0;
  
  	netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
  	if (!netpoll)
  		return -ENOMEM;
  
  	err = __netpoll_setup(netpoll, master);
  	if (err) {
  		kfree(netpoll);
  		goto out;
  	}
  
  	p->netpoll = netpoll;
  out:
  	return err;
  }
  
  static void dsa_slave_netpoll_cleanup(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct netpoll *netpoll = p->netpoll;
  
  	if (!netpoll)
  		return;
  
  	p->netpoll = NULL;
  
  	__netpoll_free_async(netpoll);
  }
  
  static void dsa_slave_poll_controller(struct net_device *dev)
  {
  }
  #endif
44bb765cf   Florian Fainelli   net: dsa: Impleme...
750
751
752
753
  static int dsa_slave_get_phys_port_name(struct net_device *dev,
  					char *name, size_t len)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
754
  	if (snprintf(name, len, "p%d", p->dp->index) >= len)
44bb765cf   Florian Fainelli   net: dsa: Impleme...
755
  		return -EINVAL;
3a543ef47   Florian Fainelli   net: dsa: Impleme...
756
757
758
  
  	return 0;
  }
f50f21274   Florian Fainelli   net: dsa: Add plu...
759
760
761
762
763
764
765
766
767
768
769
770
771
772
  static struct dsa_mall_tc_entry *
  dsa_slave_mall_tc_entry_find(struct dsa_slave_priv *p,
  			     unsigned long cookie)
  {
  	struct dsa_mall_tc_entry *mall_tc_entry;
  
  	list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list)
  		if (mall_tc_entry->cookie == cookie)
  			return mall_tc_entry;
  
  	return NULL;
  }
  
  static int dsa_slave_add_cls_matchall(struct net_device *dev,
f50f21274   Florian Fainelli   net: dsa: Add plu...
773
774
775
776
777
  				      struct tc_cls_matchall_offload *cls,
  				      bool ingress)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_mall_tc_entry *mall_tc_entry;
5fd9fc4e2   Jiri Pirko   net: sched: push ...
778
  	__be16 protocol = cls->common.protocol;
f50f21274   Florian Fainelli   net: dsa: Add plu...
779
780
781
782
783
784
785
786
787
788
789
  	struct dsa_switch *ds = p->dp->ds;
  	struct net *net = dev_net(dev);
  	struct dsa_slave_priv *to_p;
  	struct net_device *to_dev;
  	const struct tc_action *a;
  	int err = -EOPNOTSUPP;
  	LIST_HEAD(actions);
  	int ifindex;
  
  	if (!ds->ops->port_mirror_add)
  		return err;
3bcc0cec8   Jiri Pirko   net: sched: chang...
790
  	if (!tcf_exts_has_one_action(cls->exts))
f50f21274   Florian Fainelli   net: dsa: Add plu...
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
  		return err;
  
  	tcf_exts_to_list(cls->exts, &actions);
  	a = list_first_entry(&actions, struct tc_action, list);
  
  	if (is_tcf_mirred_egress_mirror(a) && protocol == htons(ETH_P_ALL)) {
  		struct dsa_mall_mirror_tc_entry *mirror;
  
  		ifindex = tcf_mirred_ifindex(a);
  		to_dev = __dev_get_by_index(net, ifindex);
  		if (!to_dev)
  			return -EINVAL;
  
  		if (!dsa_slave_dev_check(to_dev))
  			return -EOPNOTSUPP;
  
  		mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL);
  		if (!mall_tc_entry)
  			return -ENOMEM;
  
  		mall_tc_entry->cookie = cls->cookie;
  		mall_tc_entry->type = DSA_PORT_MALL_MIRROR;
  		mirror = &mall_tc_entry->mirror;
  
  		to_p = netdev_priv(to_dev);
  
  		mirror->to_local_port = to_p->dp->index;
  		mirror->ingress = ingress;
  
  		err = ds->ops->port_mirror_add(ds, p->dp->index, mirror,
  					       ingress);
  		if (err) {
  			kfree(mall_tc_entry);
  			return err;
  		}
  
  		list_add_tail(&mall_tc_entry->list, &p->mall_tc_list);
  	}
  
  	return 0;
  }
  
  static void dsa_slave_del_cls_matchall(struct net_device *dev,
  				       struct tc_cls_matchall_offload *cls)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_mall_tc_entry *mall_tc_entry;
  	struct dsa_switch *ds = p->dp->ds;
  
  	if (!ds->ops->port_mirror_del)
  		return;
  
  	mall_tc_entry = dsa_slave_mall_tc_entry_find(p, cls->cookie);
  	if (!mall_tc_entry)
  		return;
  
  	list_del(&mall_tc_entry->list);
  
  	switch (mall_tc_entry->type) {
  	case DSA_PORT_MALL_MIRROR:
  		ds->ops->port_mirror_del(ds, p->dp->index,
  					 &mall_tc_entry->mirror);
  		break;
  	default:
  		WARN_ON(1);
  	}
  
  	kfree(mall_tc_entry);
  }
3fbae382f   Jiri Pirko   dsa: push cls_mat...
860
  static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev,
3fbae382f   Jiri Pirko   dsa: push cls_mat...
861
  					   struct tc_cls_matchall_offload *cls)
f50f21274   Florian Fainelli   net: dsa: Add plu...
862
  {
a2e8da937   Jiri Pirko   net: sched: use n...
863
864
865
866
867
868
869
870
  	bool ingress;
  
  	if (is_classid_clsact_ingress(cls->common.classid))
  		ingress = true;
  	else if (is_classid_clsact_egress(cls->common.classid))
  		ingress = false;
  	else
  		return -EOPNOTSUPP;
a5fcf8a6c   Jiri Pirko   net: propagate tc...
871

5fd9fc4e2   Jiri Pirko   net: sched: push ...
872
  	if (cls->common.chain_index)
a5fcf8a6c   Jiri Pirko   net: propagate tc...
873
  		return -EOPNOTSUPP;
f50f21274   Florian Fainelli   net: dsa: Add plu...
874

3fbae382f   Jiri Pirko   dsa: push cls_mat...
875
876
  	switch (cls->command) {
  	case TC_CLSMATCHALL_REPLACE:
5fd9fc4e2   Jiri Pirko   net: sched: push ...
877
  		return dsa_slave_add_cls_matchall(dev, cls, ingress);
3fbae382f   Jiri Pirko   dsa: push cls_mat...
878
879
880
881
882
883
884
885
886
  	case TC_CLSMATCHALL_DESTROY:
  		dsa_slave_del_cls_matchall(dev, cls);
  		return 0;
  	default:
  		return -EOPNOTSUPP;
  	}
  }
  
  static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type,
de4784ca0   Jiri Pirko   net: sched: get r...
887
  			      void *type_data)
3fbae382f   Jiri Pirko   dsa: push cls_mat...
888
  {
2572ac53c   Jiri Pirko   net: sched: make ...
889
  	switch (type) {
ade9b6588   Jiri Pirko   net: sched: renam...
890
  	case TC_SETUP_CLSMATCHALL:
de4784ca0   Jiri Pirko   net: sched: get r...
891
  		return dsa_slave_setup_tc_cls_matchall(dev, type_data);
f50f21274   Florian Fainelli   net: dsa: Add plu...
892
  	default:
a5fcf8a6c   Jiri Pirko   net: propagate tc...
893
  		return -EOPNOTSUPP;
f50f21274   Florian Fainelli   net: dsa: Add plu...
894
  	}
f50f21274   Florian Fainelli   net: dsa: Add plu...
895
  }
f613ed665   Florian Fainelli   net: dsa: Add sup...
896
897
898
899
  static void dsa_slave_get_stats64(struct net_device *dev,
  				  struct rtnl_link_stats64 *stats)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
900
  	struct pcpu_sw_netstats *s;
f613ed665   Florian Fainelli   net: dsa: Add sup...
901
  	unsigned int start;
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
902
  	int i;
f613ed665   Florian Fainelli   net: dsa: Add sup...
903
904
  
  	netdev_stats_to_stats64(stats, &dev->stats);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
  	for_each_possible_cpu(i) {
  		u64 tx_packets, tx_bytes, rx_packets, rx_bytes;
  
  		s = per_cpu_ptr(p->stats64, i);
  		do {
  			start = u64_stats_fetch_begin_irq(&s->syncp);
  			tx_packets = s->tx_packets;
  			tx_bytes = s->tx_bytes;
  			rx_packets = s->rx_packets;
  			rx_bytes = s->rx_bytes;
  		} while (u64_stats_fetch_retry_irq(&s->syncp, start));
  
  		stats->tx_packets += tx_packets;
  		stats->tx_bytes += tx_bytes;
  		stats->rx_packets += rx_packets;
  		stats->rx_bytes += rx_bytes;
  	}
f613ed665   Florian Fainelli   net: dsa: Add sup...
922
  }
af42192c4   Florian Fainelli   net: dsa: Add ini...
923
924
925
926
927
928
  void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops)
  {
  	ops->get_sset_count = dsa_cpu_port_get_sset_count;
  	ops->get_ethtool_stats = dsa_cpu_port_get_ethtool_stats;
  	ops->get_strings = dsa_cpu_port_get_strings;
  }
bf9f26485   Florian Fainelli   net: dsa: Hook {g...
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
  static int dsa_slave_get_rxnfc(struct net_device *dev,
  			       struct ethtool_rxnfc *nfc, u32 *rule_locs)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->dp->ds;
  
  	if (!ds->ops->get_rxnfc)
  		return -EOPNOTSUPP;
  
  	return ds->ops->get_rxnfc(ds, p->dp->index, nfc, rule_locs);
  }
  
  static int dsa_slave_set_rxnfc(struct net_device *dev,
  			       struct ethtool_rxnfc *nfc)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->dp->ds;
  
  	if (!ds->ops->set_rxnfc)
  		return -EOPNOTSUPP;
  
  	return ds->ops->set_rxnfc(ds, p->dp->index, nfc);
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
952
  static const struct ethtool_ops dsa_slave_ethtool_ops = {
91da11f87   Lennert Buytenhek   net: Distributed ...
953
  	.get_drvinfo		= dsa_slave_get_drvinfo,
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
954
955
  	.get_regs_len		= dsa_slave_get_regs_len,
  	.get_regs		= dsa_slave_get_regs,
91da11f87   Lennert Buytenhek   net: Distributed ...
956
957
  	.nway_reset		= dsa_slave_nway_reset,
  	.get_link		= dsa_slave_get_link,
6793abb4e   Guenter Roeck   net: dsa: Add sup...
958
959
960
  	.get_eeprom_len		= dsa_slave_get_eeprom_len,
  	.get_eeprom		= dsa_slave_get_eeprom,
  	.set_eeprom		= dsa_slave_set_eeprom,
91da11f87   Lennert Buytenhek   net: Distributed ...
961
962
963
  	.get_strings		= dsa_slave_get_strings,
  	.get_ethtool_stats	= dsa_slave_get_ethtool_stats,
  	.get_sset_count		= dsa_slave_get_sset_count,
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
964
965
  	.set_wol		= dsa_slave_set_wol,
  	.get_wol		= dsa_slave_get_wol,
7905288f0   Florian Fainelli   net: dsa: allow s...
966
967
  	.set_eee		= dsa_slave_set_eee,
  	.get_eee		= dsa_slave_get_eee,
bb10bb3ea   Philippe Reynes   net: dsa: slave: ...
968
969
  	.get_link_ksettings	= dsa_slave_get_link_ksettings,
  	.set_link_ksettings	= dsa_slave_set_link_ksettings,
bf9f26485   Florian Fainelli   net: dsa: Hook {g...
970
971
  	.get_rxnfc		= dsa_slave_get_rxnfc,
  	.set_rxnfc		= dsa_slave_set_rxnfc,
91da11f87   Lennert Buytenhek   net: Distributed ...
972
  };
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
973
  static const struct net_device_ops dsa_slave_netdev_ops = {
d442ad4ab   Stephen Hemminger   dsa: convert to n...
974
975
  	.ndo_open	 	= dsa_slave_open,
  	.ndo_stop		= dsa_slave_close,
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
976
  	.ndo_start_xmit		= dsa_slave_xmit,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
977
978
  	.ndo_change_rx_flags	= dsa_slave_change_rx_flags,
  	.ndo_set_rx_mode	= dsa_slave_set_rx_mode,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
979
  	.ndo_set_mac_address	= dsa_slave_set_mac_address,
37b8da1a3   Arkadi Sharshevsky   net: dsa: Move FD...
980
981
  	.ndo_fdb_add		= dsa_legacy_fdb_add,
  	.ndo_fdb_del		= dsa_legacy_fdb_del,
2bedde1ab   Arkadi Sharshevsky   net: dsa: Move FD...
982
  	.ndo_fdb_dump		= dsa_slave_fdb_dump,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
983
  	.ndo_do_ioctl		= dsa_slave_ioctl,
abd2be00d   Nicolas Dichtel   dsa: implement nd...
984
  	.ndo_get_iflink		= dsa_slave_get_iflink,
04ff53f96   Florian Fainelli   net: dsa: Add net...
985
986
987
988
989
  #ifdef CONFIG_NET_POLL_CONTROLLER
  	.ndo_netpoll_setup	= dsa_slave_netpoll_setup,
  	.ndo_netpoll_cleanup	= dsa_slave_netpoll_cleanup,
  	.ndo_poll_controller	= dsa_slave_poll_controller,
  #endif
44bb765cf   Florian Fainelli   net: dsa: Impleme...
990
  	.ndo_get_phys_port_name	= dsa_slave_get_phys_port_name,
f50f21274   Florian Fainelli   net: dsa: Add plu...
991
  	.ndo_setup_tc		= dsa_slave_setup_tc,
f613ed665   Florian Fainelli   net: dsa: Add sup...
992
  	.ndo_get_stats64	= dsa_slave_get_stats64,
98237d433   Scott Feldman   switchdev: use ne...
993
  };
9d47c0a2d   Jiri Pirko   switchdev: s/swde...
994
  static const struct switchdev_ops dsa_slave_switchdev_ops = {
f8e20a9f8   Scott Feldman   switchdev: conver...
995
  	.switchdev_port_attr_get	= dsa_slave_port_attr_get,
356360625   Scott Feldman   switchdev: conver...
996
  	.switchdev_port_attr_set	= dsa_slave_port_attr_set,
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
997
998
  	.switchdev_port_obj_add		= dsa_slave_port_obj_add,
  	.switchdev_port_obj_del		= dsa_slave_port_obj_del,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
999
  };
91da11f87   Lennert Buytenhek   net: Distributed ...
1000

f37db85d0   Florian Fainelli   net: dsa: Set a "...
1001
1002
1003
  static struct device_type dsa_type = {
  	.name	= "dsa",
  };
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1004
1005
1006
  static void dsa_slave_adjust_link(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
1007
  	struct dsa_switch *ds = p->dp->ds;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
  	unsigned int status_changed = 0;
  
  	if (p->old_link != p->phy->link) {
  		status_changed = 1;
  		p->old_link = p->phy->link;
  	}
  
  	if (p->old_duplex != p->phy->duplex) {
  		status_changed = 1;
  		p->old_duplex = p->phy->duplex;
  	}
  
  	if (p->old_pause != p->phy->pause) {
  		status_changed = 1;
  		p->old_pause = p->phy->pause;
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
1024
  	if (ds->ops->adjust_link && status_changed)
afdcf151c   Vivien Didelot   net: dsa: store a...
1025
  		ds->ops->adjust_link(ds, p->dp->index, p->phy);
ec9436bae   Florian Fainelli   net: dsa: allow d...
1026

0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1027
1028
1029
  	if (status_changed)
  		phy_print_status(p->phy);
  }
ce31b31c6   Florian Fainelli   net: dsa: allow u...
1030
1031
1032
  static int dsa_slave_fixed_link_update(struct net_device *dev,
  				       struct fixed_phy_status *status)
  {
b71be352f   Andrew Lunn   dsa: slave: Don't...
1033
1034
1035
1036
1037
  	struct dsa_slave_priv *p;
  	struct dsa_switch *ds;
  
  	if (dev) {
  		p = netdev_priv(dev);
afdcf151c   Vivien Didelot   net: dsa: store a...
1038
  		ds = p->dp->ds;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
1039
  		if (ds->ops->fixed_link_update)
afdcf151c   Vivien Didelot   net: dsa: store a...
1040
  			ds->ops->fixed_link_update(ds, p->dp->index, status);
b71be352f   Andrew Lunn   dsa: slave: Don't...
1041
  	}
ce31b31c6   Florian Fainelli   net: dsa: allow u...
1042
1043
1044
  
  	return 0;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
1045
  /* slave device setup *******************************************************/
c305c1651   Florian Fainelli   net: dsa: move PH...
1046
  static int dsa_slave_phy_connect(struct dsa_slave_priv *p,
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1047
1048
  				 struct net_device *slave_dev,
  				 int addr)
c305c1651   Florian Fainelli   net: dsa: move PH...
1049
  {
afdcf151c   Vivien Didelot   net: dsa: store a...
1050
  	struct dsa_switch *ds = p->dp->ds;
c305c1651   Florian Fainelli   net: dsa: move PH...
1051

7f854420f   Andrew Lunn   phy: Add API for ...
1052
  	p->phy = mdiobus_get_phy(ds->slave_mii_bus, addr);
d25b8e742   Russell King   net: dsa: better ...
1053
1054
1055
  	if (!p->phy) {
  		netdev_err(slave_dev, "no phy at %d
  ", addr);
c305c1651   Florian Fainelli   net: dsa: move PH...
1056
  		return -ENODEV;
d25b8e742   Russell King   net: dsa: better ...
1057
  	}
c305c1651   Florian Fainelli   net: dsa: move PH...
1058
1059
  
  	/* Use already configured phy mode */
211c504a4   Florian Fainelli   net: dsa: Do not ...
1060
1061
  	if (p->phy_interface == PHY_INTERFACE_MODE_NA)
  		p->phy_interface = p->phy->interface;
4078b76ca   Florian Fainelli   net: dsa: Check r...
1062
1063
  	return phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
  				  p->phy_interface);
c305c1651   Florian Fainelli   net: dsa: move PH...
1064
  }
9697f1cde   Florian Fainelli   net: dsa: propaga...
1065
  static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1066
1067
  				struct net_device *slave_dev)
  {
afdcf151c   Vivien Didelot   net: dsa: store a...
1068
  	struct dsa_switch *ds = p->dp->ds;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1069
  	struct device_node *phy_dn, *port_dn;
ce31b31c6   Florian Fainelli   net: dsa: allow u...
1070
  	bool phy_is_fixed = false;
6819563e6   Florian Fainelli   net: dsa: allow s...
1071
  	u32 phy_flags = 0;
19334920e   Guenter Roeck   net: dsa: Set val...
1072
  	int mode, ret;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1073

afdcf151c   Vivien Didelot   net: dsa: store a...
1074
  	port_dn = p->dp->dn;
19334920e   Guenter Roeck   net: dsa: Set val...
1075
1076
1077
1078
  	mode = of_get_phy_mode(port_dn);
  	if (mode < 0)
  		mode = PHY_INTERFACE_MODE_NA;
  	p->phy_interface = mode;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1079
1080
  
  	phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
0d8f3c671   Johan Hovold   net: dsa: slave: ...
1081
  	if (!phy_dn && of_phy_is_fixed_link(port_dn)) {
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1082
1083
1084
1085
1086
  		/* In the case of a fixed PHY, the DT node associated
  		 * to the fixed PHY is the Port DT node
  		 */
  		ret = of_phy_register_fixed_link(port_dn);
  		if (ret) {
d25b8e742   Russell King   net: dsa: better ...
1087
1088
  			netdev_err(slave_dev, "failed to register fixed PHY: %d
  ", ret);
9697f1cde   Florian Fainelli   net: dsa: propaga...
1089
  			return ret;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1090
  		}
ce31b31c6   Florian Fainelli   net: dsa: allow u...
1091
  		phy_is_fixed = true;
0d8f3c671   Johan Hovold   net: dsa: slave: ...
1092
  		phy_dn = of_node_get(port_dn);
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1093
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
1094
  	if (ds->ops->get_phy_flags)
afdcf151c   Vivien Didelot   net: dsa: store a...
1095
  		phy_flags = ds->ops->get_phy_flags(ds, p->dp->index);
6819563e6   Florian Fainelli   net: dsa: allow s...
1096

cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1097
  	if (phy_dn) {
d25b8e742   Russell King   net: dsa: better ...
1098
  		int phy_id = of_mdio_parse_addr(&slave_dev->dev, phy_dn);
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1099
1100
1101
1102
1103
  		/* If this PHY address is part of phys_mii_mask, which means
  		 * that we need to divert reads and writes to/from it, then we
  		 * want to bind this device using the slave MII bus created by
  		 * DSA to make that happen.
  		 */
d25b8e742   Russell King   net: dsa: better ...
1104
1105
1106
1107
1108
1109
  		if (!phy_is_fixed && phy_id >= 0 &&
  		    (ds->phys_mii_mask & (1 << phy_id))) {
  			ret = dsa_slave_phy_connect(p, slave_dev, phy_id);
  			if (ret) {
  				netdev_err(slave_dev, "failed to connect to phy%d: %d
  ", phy_id, ret);
0d8f3c671   Johan Hovold   net: dsa: slave: ...
1110
  				of_node_put(phy_dn);
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1111
  				return ret;
d25b8e742   Russell King   net: dsa: better ...
1112
  			}
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1113
1114
1115
1116
1117
1118
  		} else {
  			p->phy = of_phy_connect(slave_dev, phy_dn,
  						dsa_slave_adjust_link,
  						phy_flags,
  						p->phy_interface);
  		}
0d8f3c671   Johan Hovold   net: dsa: slave: ...
1119
1120
  
  		of_node_put(phy_dn);
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1121
  	}
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1122

ce31b31c6   Florian Fainelli   net: dsa: allow u...
1123
1124
  	if (p->phy && phy_is_fixed)
  		fixed_phy_set_link_update(p->phy, dsa_slave_fixed_link_update);
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1125
1126
1127
  	/* We could not connect to a designated PHY, so use the switch internal
  	 * MDIO bus instead
  	 */
b31f65fb4   Andrew Lunn   net: dsa: slave: ...
1128
  	if (!p->phy) {
afdcf151c   Vivien Didelot   net: dsa: store a...
1129
  		ret = dsa_slave_phy_connect(p, slave_dev, p->dp->index);
d25b8e742   Russell King   net: dsa: better ...
1130
  		if (ret) {
afdcf151c   Vivien Didelot   net: dsa: store a...
1131
1132
1133
  			netdev_err(slave_dev, "failed to connect to port %d: %d
  ",
  				   p->dp->index, ret);
881eadabe   Johan Hovold   net: dsa: slave: ...
1134
1135
  			if (phy_is_fixed)
  				of_phy_deregister_fixed_link(port_dn);
c305c1651   Florian Fainelli   net: dsa: move PH...
1136
  			return ret;
d25b8e742   Russell King   net: dsa: better ...
1137
  		}
b31f65fb4   Andrew Lunn   net: dsa: slave: ...
1138
  	}
9697f1cde   Florian Fainelli   net: dsa: propaga...
1139

2220943a2   Andrew Lunn   phy: Centralise p...
1140
  	phy_attached_info(p->phy);
9697f1cde   Florian Fainelli   net: dsa: propaga...
1141
  	return 0;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1142
  }
448b4482c   Andrew Lunn   net: dsa: Add loc...
1143
1144
1145
1146
1147
1148
1149
1150
  static struct lock_class_key dsa_slave_netdev_xmit_lock_key;
  static void dsa_slave_set_lockdep_class_one(struct net_device *dev,
  					    struct netdev_queue *txq,
  					    void *_unused)
  {
  	lockdep_set_class(&txq->_xmit_lock,
  			  &dsa_slave_netdev_xmit_lock_key);
  }
244625491   Florian Fainelli   net: dsa: allow s...
1151
1152
1153
  int dsa_slave_suspend(struct net_device *slave_dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(slave_dev);
bcbdea137   Florian Fainelli   net: dsa: Do not ...
1154
1155
  	if (!netif_running(slave_dev))
  		return 0;
f154be241   Florian Fainelli   net: dsa: Bring b...
1156
  	netif_device_detach(slave_dev);
244625491   Florian Fainelli   net: dsa: allow s...
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
  	if (p->phy) {
  		phy_stop(p->phy);
  		p->old_pause = -1;
  		p->old_link = -1;
  		p->old_duplex = -1;
  		phy_suspend(p->phy);
  	}
  
  	return 0;
  }
  
  int dsa_slave_resume(struct net_device *slave_dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(slave_dev);
bcbdea137   Florian Fainelli   net: dsa: Do not ...
1171
1172
  	if (!netif_running(slave_dev))
  		return 0;
244625491   Florian Fainelli   net: dsa: allow s...
1173
1174
1175
1176
1177
1178
1179
1180
1181
  	netif_device_attach(slave_dev);
  
  	if (p->phy) {
  		phy_resume(p->phy);
  		phy_start(p->phy);
  	}
  
  	return 0;
  }
4cfbf09cf   Vivien Didelot   net: dsa: remove ...
1182
  int dsa_slave_create(struct dsa_port *port, const char *name)
91da11f87   Lennert Buytenhek   net: Distributed ...
1183
  {
4cfbf09cf   Vivien Didelot   net: dsa: remove ...
1184
  	struct dsa_switch *ds = port->ds;
badf3ada6   Florian Fainelli   net: dsa: Provide...
1185
  	struct dsa_switch_tree *dst = ds->dst;
83c0afaec   Andrew Lunn   net: dsa: Add new...
1186
  	struct net_device *master;
91da11f87   Lennert Buytenhek   net: Distributed ...
1187
1188
  	struct net_device *slave_dev;
  	struct dsa_slave_priv *p;
a29342e73   Florian Fainelli   net: dsa: Associa...
1189
  	struct dsa_port *cpu_dp;
91da11f87   Lennert Buytenhek   net: Distributed ...
1190
  	int ret;
a29342e73   Florian Fainelli   net: dsa: Associa...
1191
1192
  	cpu_dp = ds->dst->cpu_dp;
  	master = cpu_dp->netdev;
83c0afaec   Andrew Lunn   net: dsa: Add new...
1193

55199df6d   Florian Fainelli   net: dsa: Allow s...
1194
1195
1196
1197
1198
1199
  	if (!ds->num_tx_queues)
  		ds->num_tx_queues = 1;
  
  	slave_dev = alloc_netdev_mqs(sizeof(struct dsa_slave_priv), name,
  				     NET_NAME_UNKNOWN, ether_setup,
  				     ds->num_tx_queues, 1);
91da11f87   Lennert Buytenhek   net: Distributed ...
1200
  	if (slave_dev == NULL)
d87d6f44d   Guenter Roeck   net: dsa: Ensure ...
1201
  		return -ENOMEM;
91da11f87   Lennert Buytenhek   net: Distributed ...
1202

f50f21274   Florian Fainelli   net: dsa: Add plu...
1203
1204
  	slave_dev->features = master->vlan_features | NETIF_F_HW_TC;
  	slave_dev->hw_features |= NETIF_F_HW_TC;
7ad24ea4b   Wilfried Klaebe   net: get rid of S...
1205
  	slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
2fcc80058   Bjørn Mork   net: dsa: inherit...
1206
  	eth_hw_addr_inherit(slave_dev, master);
0a5f107b6   Phil Sutter   net: dsa: convert...
1207
  	slave_dev->priv_flags |= IFF_NO_QUEUE;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
1208
  	slave_dev->netdev_ops = &dsa_slave_netdev_ops;
9d47c0a2d   Jiri Pirko   switchdev: s/swde...
1209
  	slave_dev->switchdev_ops = &dsa_slave_switchdev_ops;
8b1efc0f8   Jarod Wilson   net: remove MTU l...
1210
1211
  	slave_dev->min_mtu = 0;
  	slave_dev->max_mtu = ETH_MAX_MTU;
f37db85d0   Florian Fainelli   net: dsa: Set a "...
1212
  	SET_NETDEV_DEVTYPE(slave_dev, &dsa_type);
d442ad4ab   Stephen Hemminger   dsa: convert to n...
1213

448b4482c   Andrew Lunn   net: dsa: Add loc...
1214
1215
  	netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one,
  				 NULL);
4cfbf09cf   Vivien Didelot   net: dsa: remove ...
1216
1217
  	SET_NETDEV_DEV(slave_dev, port->ds->dev);
  	slave_dev->dev.of_node = port->dn;
5075314e4   Alexander Duyck   dsa: Split ops up...
1218
1219
1220
  	slave_dev->vlan_features = master->vlan_features;
  
  	p = netdev_priv(slave_dev);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
1221
1222
1223
1224
1225
  	p->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
  	if (!p->stats64) {
  		free_netdev(slave_dev);
  		return -ENOMEM;
  	}
4cfbf09cf   Vivien Didelot   net: dsa: remove ...
1226
  	p->dp = port;
f50f21274   Florian Fainelli   net: dsa: Add plu...
1227
  	INIT_LIST_HEAD(&p->mall_tc_list);
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
1228
  	p->xmit = dst->tag_ops->xmit;
d442ad4ab   Stephen Hemminger   dsa: convert to n...
1229

0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1230
1231
1232
  	p->old_pause = -1;
  	p->old_link = -1;
  	p->old_duplex = -1;
4cfbf09cf   Vivien Didelot   net: dsa: remove ...
1233
  	port->netdev = slave_dev;
91da11f87   Lennert Buytenhek   net: Distributed ...
1234
1235
  
  	netif_carrier_off(slave_dev);
0071f56e4   Andrew Lunn   dsa: Register net...
1236
1237
1238
1239
  	ret = dsa_slave_phy_setup(p, slave_dev);
  	if (ret) {
  		netdev_err(master, "error %d setting up slave phy
  ", ret);
e804441cf   Florian Fainelli   net: dsa: Fix net...
1240
1241
1242
1243
1244
1245
1246
1247
1248
  		goto out_free;
  	}
  
  	ret = register_netdev(slave_dev);
  	if (ret) {
  		netdev_err(master, "error %d registering interface %s
  ",
  			   ret, slave_dev->name);
  		goto out_phy;
0071f56e4   Andrew Lunn   dsa: Register net...
1249
  	}
d87d6f44d   Guenter Roeck   net: dsa: Ensure ...
1250
  	return 0;
e804441cf   Florian Fainelli   net: dsa: Fix net...
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
  
  out_phy:
  	phy_disconnect(p->phy);
  	if (of_phy_is_fixed_link(p->dp->dn))
  		of_phy_deregister_fixed_link(p->dp->dn);
  out_free:
  	free_percpu(p->stats64);
  	free_netdev(slave_dev);
  	port->netdev = NULL;
  	return ret;
91da11f87   Lennert Buytenhek   net: Distributed ...
1261
  }
b73adef67   Florian Fainelli   net: dsa: integra...
1262

cda5c15b2   Neil Armstrong   net: dsa: move ds...
1263
1264
1265
  void dsa_slave_destroy(struct net_device *slave_dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(slave_dev);
881eadabe   Johan Hovold   net: dsa: slave: ...
1266
  	struct device_node *port_dn;
afdcf151c   Vivien Didelot   net: dsa: store a...
1267
  	port_dn = p->dp->dn;
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1268
1269
  
  	netif_carrier_off(slave_dev);
881eadabe   Johan Hovold   net: dsa: slave: ...
1270
  	if (p->phy) {
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1271
  		phy_disconnect(p->phy);
881eadabe   Johan Hovold   net: dsa: slave: ...
1272
1273
1274
1275
  
  		if (of_phy_is_fixed_link(port_dn))
  			of_phy_deregister_fixed_link(port_dn);
  	}
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1276
  	unregister_netdev(slave_dev);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
1277
  	free_percpu(p->stats64);
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1278
1279
  	free_netdev(slave_dev);
  }
b73adef67   Florian Fainelli   net: dsa: integra...
1280
1281
1282
1283
  static bool dsa_slave_dev_check(struct net_device *dev)
  {
  	return dev->netdev_ops == &dsa_slave_netdev_ops;
  }
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1284
1285
  static int dsa_slave_changeupper(struct net_device *dev,
  				 struct netdev_notifier_changeupper_info *info)
b73adef67   Florian Fainelli   net: dsa: integra...
1286
  {
17d7802b7   Vivien Didelot   net: dsa: change ...
1287
1288
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_port *dp = p->dp;
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1289
  	int err = NOTIFY_DONE;
b73adef67   Florian Fainelli   net: dsa: integra...
1290

8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1291
1292
  	if (netif_is_bridge_master(info->upper_dev)) {
  		if (info->linking) {
17d7802b7   Vivien Didelot   net: dsa: change ...
1293
  			err = dsa_port_bridge_join(dp, info->upper_dev);
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1294
1295
  			err = notifier_from_errno(err);
  		} else {
17d7802b7   Vivien Didelot   net: dsa: change ...
1296
  			dsa_port_bridge_leave(dp, info->upper_dev);
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1297
  			err = NOTIFY_OK;
6debb68a2   Vivien Didelot   net: dsa: refine ...
1298
  		}
6debb68a2   Vivien Didelot   net: dsa: refine ...
1299
  	}
b73adef67   Florian Fainelli   net: dsa: integra...
1300

8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1301
  	return err;
6debb68a2   Vivien Didelot   net: dsa: refine ...
1302
  }
b73adef67   Florian Fainelli   net: dsa: integra...
1303

88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1304
1305
  static int dsa_slave_netdevice_event(struct notifier_block *nb,
  				     unsigned long event, void *ptr)
6debb68a2   Vivien Didelot   net: dsa: refine ...
1306
1307
  {
  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1308
1309
1310
1311
1312
  	if (dev->netdev_ops != &dsa_slave_netdev_ops)
  		return NOTIFY_DONE;
  
  	if (event == NETDEV_CHANGEUPPER)
  		return dsa_slave_changeupper(dev, ptr);
b73adef67   Florian Fainelli   net: dsa: integra...
1313

b73adef67   Florian Fainelli   net: dsa: integra...
1314
1315
  	return NOTIFY_DONE;
  }
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1316

c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
  struct dsa_switchdev_event_work {
  	struct work_struct work;
  	struct switchdev_notifier_fdb_info fdb_info;
  	struct net_device *dev;
  	unsigned long event;
  };
  
  static void dsa_slave_switchdev_event_work(struct work_struct *work)
  {
  	struct dsa_switchdev_event_work *switchdev_work =
  		container_of(work, struct dsa_switchdev_event_work, work);
  	struct net_device *dev = switchdev_work->dev;
  	struct switchdev_notifier_fdb_info *fdb_info;
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	int err;
  
  	rtnl_lock();
  	switch (switchdev_work->event) {
  	case SWITCHDEV_FDB_ADD_TO_DEVICE:
  		fdb_info = &switchdev_work->fdb_info;
  		err = dsa_port_fdb_add(p->dp, fdb_info->addr, fdb_info->vid);
  		if (err) {
  			netdev_dbg(dev, "fdb add failed err=%d
  ", err);
  			break;
  		}
  		call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
  					 &fdb_info->info);
  		break;
  
  	case SWITCHDEV_FDB_DEL_TO_DEVICE:
  		fdb_info = &switchdev_work->fdb_info;
  		err = dsa_port_fdb_del(p->dp, fdb_info->addr, fdb_info->vid);
  		if (err) {
  			netdev_dbg(dev, "fdb del failed err=%d
  ", err);
  			dev_close(dev);
  		}
  		break;
  	}
  	rtnl_unlock();
  
  	kfree(switchdev_work->fdb_info.addr);
  	kfree(switchdev_work);
  	dev_put(dev);
  }
  
  static int
  dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work *
  				  switchdev_work,
  				  const struct switchdev_notifier_fdb_info *
  				  fdb_info)
  {
  	memcpy(&switchdev_work->fdb_info, fdb_info,
  	       sizeof(switchdev_work->fdb_info));
  	switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
  	if (!switchdev_work->fdb_info.addr)
  		return -ENOMEM;
  	ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
  			fdb_info->addr);
  	return 0;
  }
  
  /* Called under rcu_read_lock() */
  static int dsa_slave_switchdev_event(struct notifier_block *unused,
  				     unsigned long event, void *ptr)
  {
  	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
  	struct dsa_switchdev_event_work *switchdev_work;
  
  	if (!dsa_slave_dev_check(dev))
  		return NOTIFY_DONE;
  
  	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
  	if (!switchdev_work)
  		return NOTIFY_BAD;
  
  	INIT_WORK(&switchdev_work->work,
  		  dsa_slave_switchdev_event_work);
  	switchdev_work->dev = dev;
  	switchdev_work->event = event;
  
  	switch (event) {
  	case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */
  	case SWITCHDEV_FDB_DEL_TO_DEVICE:
  		if (dsa_slave_switchdev_fdb_work_init(switchdev_work,
  						      ptr))
  			goto err_fdb_work_init;
  		dev_hold(dev);
  		break;
  	default:
  		kfree(switchdev_work);
  		return NOTIFY_DONE;
  	}
  
  	dsa_schedule_work(&switchdev_work->work);
  	return NOTIFY_OK;
  
  err_fdb_work_init:
  	kfree(switchdev_work);
  	return NOTIFY_BAD;
  }
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1419
  static struct notifier_block dsa_slave_nb __read_mostly = {
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1420
1421
1422
1423
1424
  	.notifier_call  = dsa_slave_netdevice_event,
  };
  
  static struct notifier_block dsa_slave_switchdev_notifier = {
  	.notifier_call = dsa_slave_switchdev_event,
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1425
1426
1427
1428
  };
  
  int dsa_slave_register_notifier(void)
  {
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
  	int err;
  
  	err = register_netdevice_notifier(&dsa_slave_nb);
  	if (err)
  		return err;
  
  	err = register_switchdev_notifier(&dsa_slave_switchdev_notifier);
  	if (err)
  		goto err_switchdev_nb;
  
  	return 0;
  
  err_switchdev_nb:
  	unregister_netdevice_notifier(&dsa_slave_nb);
  	return err;
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1444
1445
1446
1447
1448
  }
  
  void dsa_slave_unregister_notifier(void)
  {
  	int err;
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1449
1450
1451
1452
  	err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
  	if (err)
  		pr_err("DSA: failed to unregister switchdev notifier (%d)
  ", err);
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1453
1454
1455
1456
1457
  	err = unregister_netdevice_notifier(&dsa_slave_nb);
  	if (err)
  		pr_err("DSA: failed to unregister slave notifier (%d)
  ", err);
  }