Blame view

net/dsa/slave.c 33 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>
b73adef67   Florian Fainelli   net: dsa: integra...
19
  #include <net/rtnetlink.h>
98237d433   Scott Feldman   switchdev: use ne...
20
  #include <net/switchdev.h>
b73adef67   Florian Fainelli   net: dsa: integra...
21
  #include <linux/if_bridge.h>
04ff53f96   Florian Fainelli   net: dsa: Add net...
22
  #include <linux/netpoll.h>
91da11f87   Lennert Buytenhek   net: Distributed ...
23
24
25
26
27
28
  #include "dsa_priv.h"
  
  /* 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...
29
  	if (ds->phys_mii_mask & (1 << addr))
9d490b4ee   Vivien Didelot   net: dsa: rename ...
30
  		return ds->ops->phy_read(ds, addr, reg);
91da11f87   Lennert Buytenhek   net: Distributed ...
31
32
33
34
35
36
37
  
  	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...
38
  	if (ds->phys_mii_mask & (1 << addr))
9d490b4ee   Vivien Didelot   net: dsa: rename ...
39
  		return ds->ops->phy_write(ds, addr, reg, val);
91da11f87   Lennert Buytenhek   net: Distributed ...
40
41
42
43
44
45
46
47
48
49
  
  	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...
50
51
  	snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d",
  		 ds->dst->tree, ds->index);
c33063d6a   Andrew Lunn   dsa: Remove maste...
52
  	ds->slave_mii_bus->parent = ds->dev;
24df8986f   Vivien Didelot   net: dsa: set sla...
53
  	ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
91da11f87   Lennert Buytenhek   net: Distributed ...
54
55
56
57
  }
  
  
  /* slave device handling ****************************************************/
abd2be00d   Nicolas Dichtel   dsa: implement nd...
58
  static int dsa_slave_get_iflink(const struct net_device *dev)
c08408015   Lennert Buytenhek   dsa: set ->iflink...
59
60
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
c08408015   Lennert Buytenhek   dsa: set ->iflink...
61

abd2be00d   Nicolas Dichtel   dsa: implement nd...
62
  	return p->parent->dst->master_netdev->ifindex;
c08408015   Lennert Buytenhek   dsa: set ->iflink...
63
  }
b73adef67   Florian Fainelli   net: dsa: integra...
64
65
66
67
  static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p)
  {
  	return !!p->bridge_dev;
  }
4acfee814   Vivien Didelot   net: dsa: add por...
68
69
  static void dsa_port_set_stp_state(struct dsa_switch *ds, int port, u8 state)
  {
732f794c1   Vivien Didelot   net: dsa: add por...
70
  	struct dsa_port *dp = &ds->ports[port];
4acfee814   Vivien Didelot   net: dsa: add por...
71
72
  	if (ds->ops->port_stp_state_set)
  		ds->ops->port_stp_state_set(ds, port, state);
732f794c1   Vivien Didelot   net: dsa: add por...
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  
  	if (ds->ops->port_fast_age) {
  		/* Fast age FDB entries or flush appropriate forwarding database
  		 * for the given port, if we are moving it from Learning or
  		 * Forwarding state, to Disabled or Blocking or Listening state.
  		 */
  
  		if ((dp->stp_state == BR_STATE_LEARNING ||
  		     dp->stp_state == BR_STATE_FORWARDING) &&
  		    (state == BR_STATE_DISABLED ||
  		     state == BR_STATE_BLOCKING ||
  		     state == BR_STATE_LISTENING))
  			ds->ops->port_fast_age(ds, port);
  	}
  
  	dp->stp_state = state;
4acfee814   Vivien Didelot   net: dsa: add por...
89
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
90
91
  static int dsa_slave_open(struct net_device *dev)
  {
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
92
  	struct dsa_slave_priv *p = netdev_priv(dev);
e84665c9c   Lennert Buytenhek   dsa: add switch c...
93
  	struct net_device *master = p->parent->dst->master_netdev;
b2f2af21e   Florian Fainelli   net: dsa: allow e...
94
  	struct dsa_switch *ds = p->parent;
b73adef67   Florian Fainelli   net: dsa: integra...
95
96
  	u8 stp_state = dsa_port_is_bridged(p) ?
  			BR_STATE_BLOCKING : BR_STATE_FORWARDING;
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
97
98
99
100
  	int err;
  
  	if (!(master->flags & IFF_UP))
  		return -ENETDOWN;
8feedbb4a   Joe Perches   dsa: Convert comp...
101
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) {
a748ee242   Jiri Pirko   net: move address...
102
  		err = dev_uc_add(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  		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 ...
117
118
  	if (ds->ops->port_enable) {
  		err = ds->ops->port_enable(ds, p->port, p->phy);
b2f2af21e   Florian Fainelli   net: dsa: allow e...
119
120
121
  		if (err)
  			goto clear_promisc;
  	}
4acfee814   Vivien Didelot   net: dsa: add por...
122
  	dsa_port_set_stp_state(ds, p->port, stp_state);
b73adef67   Florian Fainelli   net: dsa: integra...
123

f7f1de51e   Florian Fainelli   net: dsa: start a...
124
125
  	if (p->phy)
  		phy_start(p->phy);
91da11f87   Lennert Buytenhek   net: Distributed ...
126
  	return 0;
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
127

b2f2af21e   Florian Fainelli   net: dsa: allow e...
128
129
  clear_promisc:
  	if (dev->flags & IFF_PROMISC)
4fdeddfe0   Gilad Ben-Yossef   dsa: fix promiscu...
130
  		dev_set_promiscuity(master, -1);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
131
132
133
134
  clear_allmulti:
  	if (dev->flags & IFF_ALLMULTI)
  		dev_set_allmulti(master, -1);
  del_unicast:
8feedbb4a   Joe Perches   dsa: Convert comp...
135
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
a748ee242   Jiri Pirko   net: move address...
136
  		dev_uc_del(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
137
138
  out:
  	return err;
91da11f87   Lennert Buytenhek   net: Distributed ...
139
140
141
142
  }
  
  static int dsa_slave_close(struct net_device *dev)
  {
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
143
  	struct dsa_slave_priv *p = netdev_priv(dev);
e84665c9c   Lennert Buytenhek   dsa: add switch c...
144
  	struct net_device *master = p->parent->dst->master_netdev;
b2f2af21e   Florian Fainelli   net: dsa: allow e...
145
  	struct dsa_switch *ds = p->parent;
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
146

f7f1de51e   Florian Fainelli   net: dsa: start a...
147
148
  	if (p->phy)
  		phy_stop(p->phy);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
149
  	dev_mc_unsync(master, dev);
a748ee242   Jiri Pirko   net: move address...
150
  	dev_uc_unsync(master, dev);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
151
152
153
154
  	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...
155
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
a748ee242   Jiri Pirko   net: move address...
156
  		dev_uc_del(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
157

9d490b4ee   Vivien Didelot   net: dsa: rename ...
158
159
  	if (ds->ops->port_disable)
  		ds->ops->port_disable(ds, p->port, p->phy);
b2f2af21e   Florian Fainelli   net: dsa: allow e...
160

4acfee814   Vivien Didelot   net: dsa: add por...
161
  	dsa_port_set_stp_state(ds, p->port, BR_STATE_DISABLED);
b73adef67   Florian Fainelli   net: dsa: integra...
162

91da11f87   Lennert Buytenhek   net: Distributed ...
163
164
165
166
167
168
  	return 0;
  }
  
  static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
e84665c9c   Lennert Buytenhek   dsa: add switch c...
169
  	struct net_device *master = p->parent->dst->master_netdev;
91da11f87   Lennert Buytenhek   net: Distributed ...
170
171
172
173
174
175
176
177
178
179
  
  	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);
e84665c9c   Lennert Buytenhek   dsa: add switch c...
180
  	struct net_device *master = p->parent->dst->master_netdev;
91da11f87   Lennert Buytenhek   net: Distributed ...
181
182
  
  	dev_mc_sync(master, dev);
a748ee242   Jiri Pirko   net: move address...
183
  	dev_uc_sync(master, dev);
91da11f87   Lennert Buytenhek   net: Distributed ...
184
  }
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
185
  static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
91da11f87   Lennert Buytenhek   net: Distributed ...
186
  {
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
187
  	struct dsa_slave_priv *p = netdev_priv(dev);
e84665c9c   Lennert Buytenhek   dsa: add switch c...
188
  	struct net_device *master = p->parent->dst->master_netdev;
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
189
190
191
192
193
194
195
196
  	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...
197
  	if (!ether_addr_equal(addr->sa_data, master->dev_addr)) {
a748ee242   Jiri Pirko   net: move address...
198
  		err = dev_uc_add(master, addr->sa_data);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
199
200
201
  		if (err < 0)
  			return err;
  	}
8feedbb4a   Joe Perches   dsa: Convert comp...
202
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
a748ee242   Jiri Pirko   net: move address...
203
  		dev_uc_del(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
204
205
  
  out:
d08f161a1   Joe Perches   dsa: Use ether_ad...
206
  	ether_addr_copy(dev->dev_addr, addr->sa_data);
91da11f87   Lennert Buytenhek   net: Distributed ...
207
208
209
  
  	return 0;
  }
111495361   Vivien Didelot   net: dsa: add sup...
210
  static int dsa_slave_port_vlan_add(struct net_device *dev,
8f24f3095   Jiri Pirko   switchdev: rename...
211
  				   const struct switchdev_obj_port_vlan *vlan,
f8db83486   Jiri Pirko   switchdev: move t...
212
  				   struct switchdev_trans *trans)
111495361   Vivien Didelot   net: dsa: add sup...
213
  {
111495361   Vivien Didelot   net: dsa: add sup...
214
215
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
111495361   Vivien Didelot   net: dsa: add sup...
216

79a62eb22   Jiri Pirko   dsa: use prepare/...
217
  	if (switchdev_trans_ph_prepare(trans)) {
9d490b4ee   Vivien Didelot   net: dsa: rename ...
218
  		if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
111495361   Vivien Didelot   net: dsa: add sup...
219
  			return -EOPNOTSUPP;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
220
  		return ds->ops->port_vlan_prepare(ds, p->port, vlan, trans);
111495361   Vivien Didelot   net: dsa: add sup...
221
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
222
  	ds->ops->port_vlan_add(ds, p->port, vlan, trans);
4d5770b39   Vivien Didelot   net: dsa: make th...
223

111495361   Vivien Didelot   net: dsa: add sup...
224
225
226
227
  	return 0;
  }
  
  static int dsa_slave_port_vlan_del(struct net_device *dev,
8f24f3095   Jiri Pirko   switchdev: rename...
228
  				   const struct switchdev_obj_port_vlan *vlan)
111495361   Vivien Didelot   net: dsa: add sup...
229
  {
111495361   Vivien Didelot   net: dsa: add sup...
230
231
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
111495361   Vivien Didelot   net: dsa: add sup...
232

9d490b4ee   Vivien Didelot   net: dsa: rename ...
233
  	if (!ds->ops->port_vlan_del)
111495361   Vivien Didelot   net: dsa: add sup...
234
  		return -EOPNOTSUPP;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
235
  	return ds->ops->port_vlan_del(ds, p->port, vlan);
111495361   Vivien Didelot   net: dsa: add sup...
236
237
238
  }
  
  static int dsa_slave_port_vlan_dump(struct net_device *dev,
8f24f3095   Jiri Pirko   switchdev: rename...
239
  				    struct switchdev_obj_port_vlan *vlan,
648b4a995   Jiri Pirko   switchdev: bring ...
240
  				    switchdev_obj_dump_cb_t *cb)
111495361   Vivien Didelot   net: dsa: add sup...
241
  {
111495361   Vivien Didelot   net: dsa: add sup...
242
243
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
111495361   Vivien Didelot   net: dsa: add sup...
244

9d490b4ee   Vivien Didelot   net: dsa: rename ...
245
246
  	if (ds->ops->port_vlan_dump)
  		return ds->ops->port_vlan_dump(ds, p->port, vlan, cb);
65aebfc00   Vivien Didelot   net: dsa: add por...
247

477b18452   Vivien Didelot   net: dsa: drop vl...
248
  	return -EOPNOTSUPP;
111495361   Vivien Didelot   net: dsa: add sup...
249
  }
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
250
  static int dsa_slave_port_fdb_add(struct net_device *dev,
52ba57cfd   Jiri Pirko   switchdev: rename...
251
  				  const struct switchdev_obj_port_fdb *fdb,
f8db83486   Jiri Pirko   switchdev: move t...
252
  				  struct switchdev_trans *trans)
cdf096976   David S. Miller   Revert "Merge bra...
253
254
255
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
146a32067   Vivien Didelot   net: dsa: add por...
256

8497aa618   Vivien Didelot   net: dsa: make th...
257
  	if (switchdev_trans_ph_prepare(trans)) {
9d490b4ee   Vivien Didelot   net: dsa: rename ...
258
  		if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add)
8497aa618   Vivien Didelot   net: dsa: make th...
259
  			return -EOPNOTSUPP;
cdf096976   David S. Miller   Revert "Merge bra...
260

9d490b4ee   Vivien Didelot   net: dsa: rename ...
261
  		return ds->ops->port_fdb_prepare(ds, p->port, fdb, trans);
8497aa618   Vivien Didelot   net: dsa: make th...
262
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
263
  	ds->ops->port_fdb_add(ds, p->port, fdb, trans);
cdf096976   David S. Miller   Revert "Merge bra...
264

8497aa618   Vivien Didelot   net: dsa: make th...
265
  	return 0;
cdf096976   David S. Miller   Revert "Merge bra...
266
  }
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
267
  static int dsa_slave_port_fdb_del(struct net_device *dev,
52ba57cfd   Jiri Pirko   switchdev: rename...
268
  				  const struct switchdev_obj_port_fdb *fdb)
cdf096976   David S. Miller   Revert "Merge bra...
269
270
271
272
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  	int ret = -EOPNOTSUPP;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
273
274
  	if (ds->ops->port_fdb_del)
  		ret = ds->ops->port_fdb_del(ds, p->port, fdb);
cdf096976   David S. Miller   Revert "Merge bra...
275
276
277
  
  	return ret;
  }
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
278
  static int dsa_slave_port_fdb_dump(struct net_device *dev,
52ba57cfd   Jiri Pirko   switchdev: rename...
279
  				   struct switchdev_obj_port_fdb *fdb,
648b4a995   Jiri Pirko   switchdev: bring ...
280
  				   switchdev_obj_dump_cb_t *cb)
cdf096976   David S. Miller   Revert "Merge bra...
281
282
283
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
cdf096976   David S. Miller   Revert "Merge bra...
284

9d490b4ee   Vivien Didelot   net: dsa: rename ...
285
286
  	if (ds->ops->port_fdb_dump)
  		return ds->ops->port_fdb_dump(ds, p->port, fdb, cb);
ea70ba980   Vivien Didelot   net: dsa: add por...
287

1a49a2fbf   Vivien Didelot   net: dsa: remove ...
288
  	return -EOPNOTSUPP;
cdf096976   David S. Miller   Revert "Merge bra...
289
  }
8df302552   Vivien Didelot   net: dsa: add MDB...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
  static int dsa_slave_port_mdb_add(struct net_device *dev,
  				  const struct switchdev_obj_port_mdb *mdb,
  				  struct switchdev_trans *trans)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  
  	if (switchdev_trans_ph_prepare(trans)) {
  		if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
  			return -EOPNOTSUPP;
  
  		return ds->ops->port_mdb_prepare(ds, p->port, mdb, trans);
  	}
  
  	ds->ops->port_mdb_add(ds, p->port, mdb, trans);
  
  	return 0;
  }
  
  static int dsa_slave_port_mdb_del(struct net_device *dev,
  				  const struct switchdev_obj_port_mdb *mdb)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  
  	if (ds->ops->port_mdb_del)
  		return ds->ops->port_mdb_del(ds, p->port, mdb);
  
  	return -EOPNOTSUPP;
  }
  
  static int dsa_slave_port_mdb_dump(struct net_device *dev,
  				   struct switchdev_obj_port_mdb *mdb,
  				   switchdev_obj_dump_cb_t *cb)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  
  	if (ds->ops->port_mdb_dump)
  		return ds->ops->port_mdb_dump(ds, p->port, mdb, cb);
  
  	return -EOPNOTSUPP;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
333
334
335
  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 ...
336
337
  
  	if (p->phy != NULL)
28b041139   Richard Cochran   net: preserve ifr...
338
  		return phy_mii_ioctl(p->phy, ifr, cmd);
91da11f87   Lennert Buytenhek   net: Distributed ...
339
340
341
  
  	return -EOPNOTSUPP;
  }
43c44a9f6   Vivien Didelot   net: dsa: make th...
342
343
344
  static int dsa_slave_stp_state_set(struct net_device *dev,
  				   const struct switchdev_attr *attr,
  				   struct switchdev_trans *trans)
b73adef67   Florian Fainelli   net: dsa: integra...
345
346
347
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
b73adef67   Florian Fainelli   net: dsa: integra...
348

43c44a9f6   Vivien Didelot   net: dsa: make th...
349
  	if (switchdev_trans_ph_prepare(trans))
9d490b4ee   Vivien Didelot   net: dsa: rename ...
350
  		return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP;
b73adef67   Florian Fainelli   net: dsa: integra...
351

4acfee814   Vivien Didelot   net: dsa: add por...
352
  	dsa_port_set_stp_state(ds, p->port, attr->u.stp_state);
43c44a9f6   Vivien Didelot   net: dsa: make th...
353
354
  
  	return 0;
b73adef67   Florian Fainelli   net: dsa: integra...
355
  }
fb2dabad6   Vivien Didelot   net: dsa: support...
356
357
358
359
360
361
362
363
364
365
  static int dsa_slave_vlan_filtering(struct net_device *dev,
  				    const struct switchdev_attr *attr,
  				    struct switchdev_trans *trans)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  
  	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
  	if (switchdev_trans_ph_prepare(trans))
  		return 0;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
366
367
  	if (ds->ops->port_vlan_filtering)
  		return ds->ops->port_vlan_filtering(ds, p->port,
fb2dabad6   Vivien Didelot   net: dsa: support...
368
369
370
371
  						    attr->u.vlan_filtering);
  
  	return 0;
  }
34a79f63b   Vivien Didelot   net: dsa: support...
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
  static int dsa_fastest_ageing_time(struct dsa_switch *ds,
  				   unsigned int ageing_time)
  {
  	int i;
  
  	for (i = 0; i < DSA_MAX_PORTS; ++i) {
  		struct dsa_port *dp = &ds->ports[i];
  
  		if (dp && dp->ageing_time && dp->ageing_time < ageing_time)
  			ageing_time = dp->ageing_time;
  	}
  
  	return ageing_time;
  }
  
  static int dsa_slave_ageing_time(struct net_device *dev,
  				 const struct switchdev_attr *attr,
  				 struct switchdev_trans *trans)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  	unsigned long ageing_jiffies = clock_t_to_jiffies(attr->u.ageing_time);
  	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
  
  	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
  	if (switchdev_trans_ph_prepare(trans))
  		return 0;
  
  	/* Keep the fastest ageing time in case of multiple bridges */
  	ds->ports[p->port].ageing_time = ageing_time;
  	ageing_time = dsa_fastest_ageing_time(ds, ageing_time);
9d490b4ee   Vivien Didelot   net: dsa: rename ...
403
404
  	if (ds->ops->set_ageing_time)
  		return ds->ops->set_ageing_time(ds, ageing_time);
34a79f63b   Vivien Didelot   net: dsa: support...
405
406
407
  
  	return 0;
  }
356360625   Scott Feldman   switchdev: conver...
408
  static int dsa_slave_port_attr_set(struct net_device *dev,
f7fadf304   Jiri Pirko   switchdev: make s...
409
  				   const struct switchdev_attr *attr,
7ea6eb3f5   Jiri Pirko   switchdev: introd...
410
  				   struct switchdev_trans *trans)
356360625   Scott Feldman   switchdev: conver...
411
  {
b8d866ac6   Vivien Didelot   net: dsa: fix pre...
412
  	int ret;
356360625   Scott Feldman   switchdev: conver...
413
414
  
  	switch (attr->id) {
1f8683987   Jiri Pirko   switchdev: rename...
415
  	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
43c44a9f6   Vivien Didelot   net: dsa: make th...
416
  		ret = dsa_slave_stp_state_set(dev, attr, trans);
356360625   Scott Feldman   switchdev: conver...
417
  		break;
fb2dabad6   Vivien Didelot   net: dsa: support...
418
419
420
  	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
  		ret = dsa_slave_vlan_filtering(dev, attr, trans);
  		break;
34a79f63b   Vivien Didelot   net: dsa: support...
421
422
423
  	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
  		ret = dsa_slave_ageing_time(dev, attr, trans);
  		break;
356360625   Scott Feldman   switchdev: conver...
424
425
426
427
428
429
430
  	default:
  		ret = -EOPNOTSUPP;
  		break;
  	}
  
  	return ret;
  }
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
431
  static int dsa_slave_port_obj_add(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
432
  				  const struct switchdev_obj *obj,
7ea6eb3f5   Jiri Pirko   switchdev: introd...
433
  				  struct switchdev_trans *trans)
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
434
435
436
437
438
439
440
  {
  	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...
441
  	switch (obj->id) {
57d80838d   Jiri Pirko   switchdev: rename...
442
  	case SWITCHDEV_OBJ_ID_PORT_FDB:
648b4a995   Jiri Pirko   switchdev: bring ...
443
444
445
  		err = dsa_slave_port_fdb_add(dev,
  					     SWITCHDEV_OBJ_PORT_FDB(obj),
  					     trans);
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
446
  		break;
8df302552   Vivien Didelot   net: dsa: add MDB...
447
448
449
450
  	case SWITCHDEV_OBJ_ID_PORT_MDB:
  		err = dsa_slave_port_mdb_add(dev, SWITCHDEV_OBJ_PORT_MDB(obj),
  					     trans);
  		break;
57d80838d   Jiri Pirko   switchdev: rename...
451
  	case SWITCHDEV_OBJ_ID_PORT_VLAN:
648b4a995   Jiri Pirko   switchdev: bring ...
452
453
454
  		err = dsa_slave_port_vlan_add(dev,
  					      SWITCHDEV_OBJ_PORT_VLAN(obj),
  					      trans);
111495361   Vivien Didelot   net: dsa: add sup...
455
  		break;
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
456
457
458
459
460
461
462
463
464
  	default:
  		err = -EOPNOTSUPP;
  		break;
  	}
  
  	return err;
  }
  
  static int dsa_slave_port_obj_del(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
465
  				  const struct switchdev_obj *obj)
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
466
467
  {
  	int err;
9e8f4a548   Jiri Pirko   switchdev: push o...
468
  	switch (obj->id) {
57d80838d   Jiri Pirko   switchdev: rename...
469
  	case SWITCHDEV_OBJ_ID_PORT_FDB:
648b4a995   Jiri Pirko   switchdev: bring ...
470
471
  		err = dsa_slave_port_fdb_del(dev,
  					     SWITCHDEV_OBJ_PORT_FDB(obj));
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
472
  		break;
8df302552   Vivien Didelot   net: dsa: add MDB...
473
474
475
  	case SWITCHDEV_OBJ_ID_PORT_MDB:
  		err = dsa_slave_port_mdb_del(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
  		break;
57d80838d   Jiri Pirko   switchdev: rename...
476
  	case SWITCHDEV_OBJ_ID_PORT_VLAN:
648b4a995   Jiri Pirko   switchdev: bring ...
477
478
  		err = dsa_slave_port_vlan_del(dev,
  					      SWITCHDEV_OBJ_PORT_VLAN(obj));
111495361   Vivien Didelot   net: dsa: add sup...
479
  		break;
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
480
481
482
483
484
485
486
487
488
  	default:
  		err = -EOPNOTSUPP;
  		break;
  	}
  
  	return err;
  }
  
  static int dsa_slave_port_obj_dump(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
489
490
  				   struct switchdev_obj *obj,
  				   switchdev_obj_dump_cb_t *cb)
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
491
492
  {
  	int err;
9e8f4a548   Jiri Pirko   switchdev: push o...
493
  	switch (obj->id) {
57d80838d   Jiri Pirko   switchdev: rename...
494
  	case SWITCHDEV_OBJ_ID_PORT_FDB:
648b4a995   Jiri Pirko   switchdev: bring ...
495
496
497
  		err = dsa_slave_port_fdb_dump(dev,
  					      SWITCHDEV_OBJ_PORT_FDB(obj),
  					      cb);
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
498
  		break;
8df302552   Vivien Didelot   net: dsa: add MDB...
499
500
501
502
  	case SWITCHDEV_OBJ_ID_PORT_MDB:
  		err = dsa_slave_port_mdb_dump(dev, SWITCHDEV_OBJ_PORT_MDB(obj),
  					      cb);
  		break;
57d80838d   Jiri Pirko   switchdev: rename...
503
  	case SWITCHDEV_OBJ_ID_PORT_VLAN:
648b4a995   Jiri Pirko   switchdev: bring ...
504
505
506
  		err = dsa_slave_port_vlan_dump(dev,
  					       SWITCHDEV_OBJ_PORT_VLAN(obj),
  					       cb);
111495361   Vivien Didelot   net: dsa: add sup...
507
  		break;
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
508
509
510
511
512
513
514
  	default:
  		err = -EOPNOTSUPP;
  		break;
  	}
  
  	return err;
  }
b73adef67   Florian Fainelli   net: dsa: integra...
515
516
517
518
519
520
521
522
  static int dsa_slave_bridge_port_join(struct net_device *dev,
  				      struct net_device *br)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  	int ret = -EOPNOTSUPP;
  
  	p->bridge_dev = br;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
523
524
  	if (ds->ops->port_bridge_join)
  		ret = ds->ops->port_bridge_join(ds, p->port, br);
b73adef67   Florian Fainelli   net: dsa: integra...
525

6debb68a2   Vivien Didelot   net: dsa: refine ...
526
  	return ret == -EOPNOTSUPP ? 0 : ret;
b73adef67   Florian Fainelli   net: dsa: integra...
527
  }
16bfa7024   Vivien Didelot   net: dsa: make po...
528
  static void dsa_slave_bridge_port_leave(struct net_device *dev)
b73adef67   Florian Fainelli   net: dsa: integra...
529
530
531
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
b73adef67   Florian Fainelli   net: dsa: integra...
532

9d490b4ee   Vivien Didelot   net: dsa: rename ...
533
534
  	if (ds->ops->port_bridge_leave)
  		ds->ops->port_bridge_leave(ds, p->port);
b73adef67   Florian Fainelli   net: dsa: integra...
535
536
537
538
539
540
  
  	p->bridge_dev = NULL;
  
  	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
  	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
  	 */
4acfee814   Vivien Didelot   net: dsa: add por...
541
  	dsa_port_set_stp_state(ds, p->port, BR_STATE_FORWARDING);
b73adef67   Florian Fainelli   net: dsa: integra...
542
  }
f8e20a9f8   Scott Feldman   switchdev: conver...
543
544
  static int dsa_slave_port_attr_get(struct net_device *dev,
  				   struct switchdev_attr *attr)
b73adef67   Florian Fainelli   net: dsa: integra...
545
546
547
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
f8e20a9f8   Scott Feldman   switchdev: conver...
548
  	switch (attr->id) {
1f8683987   Jiri Pirko   switchdev: rename...
549
  	case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
42275bd8f   Scott Feldman   switchdev: don't ...
550
551
  		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...
552
553
554
555
  		break;
  	default:
  		return -EOPNOTSUPP;
  	}
b73adef67   Florian Fainelli   net: dsa: integra...
556
557
558
  
  	return 0;
  }
04ff53f96   Florian Fainelli   net: dsa: Add net...
559
560
561
562
563
564
565
566
567
568
569
  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 ...
570
571
572
  static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
573
  	struct sk_buff *nskb;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
574

4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
575
576
  	dev->stats.tx_packets++;
  	dev->stats.tx_bytes += skb->len;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
577

4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
578
579
580
581
  	/* Transmit function may have to reallocate the original SKB */
  	nskb = p->xmit(skb, dev);
  	if (!nskb)
  		return NETDEV_TX_OK;
5aed85cec   Florian Fainelli   net: dsa: allow s...
582

04ff53f96   Florian Fainelli   net: dsa: Add net...
583
584
585
586
587
  	/* 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...
588
589
590
591
592
  	/* Queue the SKB for transmission on the parent interface, but
  	 * do not modify its EtherType
  	 */
  	nskb->dev = p->parent->dst->master_netdev;
  	dev_queue_xmit(nskb);
5aed85cec   Florian Fainelli   net: dsa: allow s...
593
594
595
  
  	return NETDEV_TX_OK;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
  /* ethtool operations *******************************************************/
  static int
  dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	int err;
  
  	err = -EOPNOTSUPP;
  	if (p->phy != NULL) {
  		err = phy_read_status(p->phy);
  		if (err == 0)
  			err = phy_ethtool_gset(p->phy, cmd);
  	}
  
  	return err;
  }
  
  static int
  dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  
  	if (p->phy != NULL)
  		return phy_ethtool_sset(p->phy, cmd);
  
  	return -EOPNOTSUPP;
  }
  
  static void dsa_slave_get_drvinfo(struct net_device *dev,
  				  struct ethtool_drvinfo *drvinfo)
  {
7826d43f2   Jiri Pirko   ethtool: fix drvi...
627
628
629
630
  	strlcpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver));
  	strlcpy(drvinfo->version, dsa_driver_version, sizeof(drvinfo->version));
  	strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
  	strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
91da11f87   Lennert Buytenhek   net: Distributed ...
631
  }
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
632
633
634
635
  static int dsa_slave_get_regs_len(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
636
637
  	if (ds->ops->get_regs_len)
  		return ds->ops->get_regs_len(ds, p->port);
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
638
639
640
641
642
643
644
645
646
  
  	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);
  	struct dsa_switch *ds = p->parent;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
647
648
  	if (ds->ops->get_regs)
  		ds->ops->get_regs(ds, p->port, regs, _p);
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
649
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
  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...
671
672
673
674
  static int dsa_slave_get_eeprom_len(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
0e5760440   Andrew Lunn   net: dsa: slave: ...
675
  	if (ds->cd && ds->cd->eeprom_len)
ff04955c2   Andrew Lunn   dsa: Rename switc...
676
  		return ds->cd->eeprom_len;
6793abb4e   Guenter Roeck   net: dsa: Add sup...
677

9d490b4ee   Vivien Didelot   net: dsa: rename ...
678
679
  	if (ds->ops->get_eeprom_len)
  		return ds->ops->get_eeprom_len(ds);
6793abb4e   Guenter Roeck   net: dsa: Add sup...
680
681
682
683
684
685
686
687
688
  
  	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);
  	struct dsa_switch *ds = p->parent;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
689
690
  	if (ds->ops->get_eeprom)
  		return ds->ops->get_eeprom(ds, eeprom, data);
6793abb4e   Guenter Roeck   net: dsa: Add sup...
691
692
693
694
695
696
697
698
699
  
  	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);
  	struct dsa_switch *ds = p->parent;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
700
701
  	if (ds->ops->set_eeprom)
  		return ds->ops->set_eeprom(ds, eeprom, data);
6793abb4e   Guenter Roeck   net: dsa: Add sup...
702
703
704
  
  	return -EOPNOTSUPP;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
705
706
707
708
709
710
711
712
713
714
715
716
717
  static void dsa_slave_get_strings(struct net_device *dev,
  				  uint32_t stringset, uint8_t *data)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  
  	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 ...
718
719
  		if (ds->ops->get_strings)
  			ds->ops->get_strings(ds, p->port, data + 4 * len);
91da11f87   Lennert Buytenhek   net: Distributed ...
720
721
  	}
  }
badf3ada6   Florian Fainelli   net: dsa: Provide...
722
723
724
725
726
727
728
729
730
731
732
733
734
735
  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;
  	struct dsa_switch *ds = dst->ds[0];
  	s8 cpu_port = dst->cpu_port;
  	int count = 0;
  
  	if (dst->master_ethtool_ops.get_sset_count) {
  		count = dst->master_ethtool_ops.get_sset_count(dev,
  							       ETH_SS_STATS);
  		dst->master_ethtool_ops.get_ethtool_stats(dev, stats, data);
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
736
737
  	if (ds->ops->get_ethtool_stats)
  		ds->ops->get_ethtool_stats(ds, cpu_port, data + count);
badf3ada6   Florian Fainelli   net: dsa: Provide...
738
739
740
741
742
743
744
745
746
747
  }
  
  static int dsa_cpu_port_get_sset_count(struct net_device *dev, int sset)
  {
  	struct dsa_switch_tree *dst = dev->dsa_ptr;
  	struct dsa_switch *ds = dst->ds[0];
  	int count = 0;
  
  	if (dst->master_ethtool_ops.get_sset_count)
  		count += dst->master_ethtool_ops.get_sset_count(dev, sset);
9d490b4ee   Vivien Didelot   net: dsa: rename ...
748
749
  	if (sset == ETH_SS_STATS && ds->ops->get_sset_count)
  		count += ds->ops->get_sset_count(ds);
badf3ada6   Florian Fainelli   net: dsa: Provide...
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
  
  	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;
  	struct dsa_switch *ds = dst->ds[0];
  	s8 cpu_port = dst->cpu_port;
  	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] = '_';
  
  	if (dst->master_ethtool_ops.get_sset_count) {
  		mcount = dst->master_ethtool_ops.get_sset_count(dev,
  								ETH_SS_STATS);
  		dst->master_ethtool_ops.get_strings(dev, stringset, data);
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
775
  	if (stringset == ETH_SS_STATS && ds->ops->get_strings) {
badf3ada6   Florian Fainelli   net: dsa: Provide...
776
777
778
779
780
  		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 ...
781
782
  		ds->ops->get_strings(ds, cpu_port, ndata);
  		count = ds->ops->get_sset_count(ds);
badf3ada6   Florian Fainelli   net: dsa: Provide...
783
784
785
786
787
788
789
  		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 ...
790
791
792
793
794
795
  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);
  	struct dsa_switch *ds = p->parent;
46e7b8d8d   Vivien Didelot   net: dsa: kill ci...
796
797
798
799
  	data[0] = dev->stats.tx_packets;
  	data[1] = dev->stats.tx_bytes;
  	data[2] = dev->stats.rx_packets;
  	data[3] = dev->stats.rx_bytes;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
800
801
  	if (ds->ops->get_ethtool_stats)
  		ds->ops->get_ethtool_stats(ds, p->port, data + 4);
91da11f87   Lennert Buytenhek   net: Distributed ...
802
803
804
805
806
807
808
809
810
811
812
  }
  
  static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  
  	if (sset == ETH_SS_STATS) {
  		int count;
  
  		count = 4;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
813
814
  		if (ds->ops->get_sset_count)
  			count += ds->ops->get_sset_count(ds);
91da11f87   Lennert Buytenhek   net: Distributed ...
815
816
817
818
819
820
  
  		return count;
  	}
  
  	return -EOPNOTSUPP;
  }
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
821
822
823
824
  static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
825
826
  	if (ds->ops->get_wol)
  		ds->ops->get_wol(ds, p->port, w);
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
827
828
829
830
831
832
833
  }
  
  static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  	int ret = -EOPNOTSUPP;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
834
835
  	if (ds->ops->set_wol)
  		ret = ds->ops->set_wol(ds, p->port, w);
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
836
837
838
  
  	return ret;
  }
7905288f0   Florian Fainelli   net: dsa: allow s...
839
840
841
842
843
  static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  	int ret;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
844
  	if (!ds->ops->set_eee)
7905288f0   Florian Fainelli   net: dsa: allow s...
845
  		return -EOPNOTSUPP;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
846
  	ret = ds->ops->set_eee(ds, p->port, p->phy, e);
7905288f0   Florian Fainelli   net: dsa: allow s...
847
848
849
850
851
852
853
854
855
856
857
858
859
860
  	if (ret)
  		return ret;
  
  	if (p->phy)
  		ret = phy_ethtool_set_eee(p->phy, e);
  
  	return ret;
  }
  
  static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->parent;
  	int ret;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
861
  	if (!ds->ops->get_eee)
7905288f0   Florian Fainelli   net: dsa: allow s...
862
  		return -EOPNOTSUPP;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
863
  	ret = ds->ops->get_eee(ds, p->port, e);
7905288f0   Florian Fainelli   net: dsa: allow s...
864
865
866
867
868
869
870
871
  	if (ret)
  		return ret;
  
  	if (p->phy)
  		ret = phy_ethtool_get_eee(p->phy, e);
  
  	return ret;
  }
04ff53f96   Florian Fainelli   net: dsa: Add net...
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
  #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);
  	struct dsa_switch *ds = p->parent;
  	struct net_device *master = ds->dst->master_netdev;
  	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
af42192c4   Florian Fainelli   net: dsa: Add ini...
914
915
916
917
918
919
  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;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
920
921
922
923
  static const struct ethtool_ops dsa_slave_ethtool_ops = {
  	.get_settings		= dsa_slave_get_settings,
  	.set_settings		= dsa_slave_set_settings,
  	.get_drvinfo		= dsa_slave_get_drvinfo,
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
924
925
  	.get_regs_len		= dsa_slave_get_regs_len,
  	.get_regs		= dsa_slave_get_regs,
91da11f87   Lennert Buytenhek   net: Distributed ...
926
927
  	.nway_reset		= dsa_slave_nway_reset,
  	.get_link		= dsa_slave_get_link,
6793abb4e   Guenter Roeck   net: dsa: Add sup...
928
929
930
  	.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 ...
931
932
933
  	.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...
934
935
  	.set_wol		= dsa_slave_set_wol,
  	.get_wol		= dsa_slave_get_wol,
7905288f0   Florian Fainelli   net: dsa: allow s...
936
937
  	.set_eee		= dsa_slave_set_eee,
  	.get_eee		= dsa_slave_get_eee,
91da11f87   Lennert Buytenhek   net: Distributed ...
938
  };
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
939
  static const struct net_device_ops dsa_slave_netdev_ops = {
d442ad4ab   Stephen Hemminger   dsa: convert to n...
940
941
  	.ndo_open	 	= dsa_slave_open,
  	.ndo_stop		= dsa_slave_close,
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
942
  	.ndo_start_xmit		= dsa_slave_xmit,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
943
944
  	.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...
945
  	.ndo_set_mac_address	= dsa_slave_set_mac_address,
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
946
947
948
  	.ndo_fdb_add		= switchdev_port_fdb_add,
  	.ndo_fdb_del		= switchdev_port_fdb_del,
  	.ndo_fdb_dump		= switchdev_port_fdb_dump,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
949
  	.ndo_do_ioctl		= dsa_slave_ioctl,
abd2be00d   Nicolas Dichtel   dsa: implement nd...
950
  	.ndo_get_iflink		= dsa_slave_get_iflink,
04ff53f96   Florian Fainelli   net: dsa: Add net...
951
952
953
954
955
  #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
111495361   Vivien Didelot   net: dsa: add sup...
956
957
958
  	.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
  	.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
  	.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
98237d433   Scott Feldman   switchdev: use ne...
959
  };
9d47c0a2d   Jiri Pirko   switchdev: s/swde...
960
  static const struct switchdev_ops dsa_slave_switchdev_ops = {
f8e20a9f8   Scott Feldman   switchdev: conver...
961
  	.switchdev_port_attr_get	= dsa_slave_port_attr_get,
356360625   Scott Feldman   switchdev: conver...
962
  	.switchdev_port_attr_set	= dsa_slave_port_attr_set,
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
963
964
965
  	.switchdev_port_obj_add		= dsa_slave_port_obj_add,
  	.switchdev_port_obj_del		= dsa_slave_port_obj_del,
  	.switchdev_port_obj_dump	= dsa_slave_port_obj_dump,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
966
  };
91da11f87   Lennert Buytenhek   net: Distributed ...
967

f37db85d0   Florian Fainelli   net: dsa: Set a "...
968
969
970
  static struct device_type dsa_type = {
  	.name	= "dsa",
  };
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
971
972
973
  static void dsa_slave_adjust_link(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
ec9436bae   Florian Fainelli   net: dsa: allow d...
974
  	struct dsa_switch *ds = p->parent;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
  	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 ...
991
992
  	if (ds->ops->adjust_link && status_changed)
  		ds->ops->adjust_link(ds, p->port, p->phy);
ec9436bae   Florian Fainelli   net: dsa: allow d...
993

0d8bcdd38   Florian Fainelli   net: dsa: allow f...
994
995
996
  	if (status_changed)
  		phy_print_status(p->phy);
  }
ce31b31c6   Florian Fainelli   net: dsa: allow u...
997
998
999
  static int dsa_slave_fixed_link_update(struct net_device *dev,
  				       struct fixed_phy_status *status)
  {
b71be352f   Andrew Lunn   dsa: slave: Don't...
1000
1001
1002
1003
1004
1005
  	struct dsa_slave_priv *p;
  	struct dsa_switch *ds;
  
  	if (dev) {
  		p = netdev_priv(dev);
  		ds = p->parent;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
1006
1007
  		if (ds->ops->fixed_link_update)
  			ds->ops->fixed_link_update(ds, p->port, status);
b71be352f   Andrew Lunn   dsa: slave: Don't...
1008
  	}
ce31b31c6   Florian Fainelli   net: dsa: allow u...
1009
1010
1011
  
  	return 0;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
1012
  /* slave device setup *******************************************************/
c305c1651   Florian Fainelli   net: dsa: move PH...
1013
  static int dsa_slave_phy_connect(struct dsa_slave_priv *p,
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1014
1015
  				 struct net_device *slave_dev,
  				 int addr)
c305c1651   Florian Fainelli   net: dsa: move PH...
1016
1017
  {
  	struct dsa_switch *ds = p->parent;
7f854420f   Andrew Lunn   phy: Add API for ...
1018
  	p->phy = mdiobus_get_phy(ds->slave_mii_bus, addr);
d25b8e742   Russell King   net: dsa: better ...
1019
1020
1021
  	if (!p->phy) {
  		netdev_err(slave_dev, "no phy at %d
  ", addr);
c305c1651   Florian Fainelli   net: dsa: move PH...
1022
  		return -ENODEV;
d25b8e742   Russell King   net: dsa: better ...
1023
  	}
c305c1651   Florian Fainelli   net: dsa: move PH...
1024
1025
  
  	/* Use already configured phy mode */
211c504a4   Florian Fainelli   net: dsa: Do not ...
1026
1027
  	if (p->phy_interface == PHY_INTERFACE_MODE_NA)
  		p->phy_interface = p->phy->interface;
c305c1651   Florian Fainelli   net: dsa: move PH...
1028
1029
1030
1031
1032
  	phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
  			   p->phy_interface);
  
  	return 0;
  }
9697f1cde   Florian Fainelli   net: dsa: propaga...
1033
  static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1034
1035
1036
  				struct net_device *slave_dev)
  {
  	struct dsa_switch *ds = p->parent;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1037
  	struct device_node *phy_dn, *port_dn;
ce31b31c6   Florian Fainelli   net: dsa: allow u...
1038
  	bool phy_is_fixed = false;
6819563e6   Florian Fainelli   net: dsa: allow s...
1039
  	u32 phy_flags = 0;
19334920e   Guenter Roeck   net: dsa: Set val...
1040
  	int mode, ret;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1041

189b0d93e   Andrew Lunn   net: dsa: Move po...
1042
  	port_dn = ds->ports[p->port].dn;
19334920e   Guenter Roeck   net: dsa: Set val...
1043
1044
1045
1046
  	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...
1047
1048
  
  	phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
0d8f3c671   Johan Hovold   net: dsa: slave: ...
1049
  	if (!phy_dn && of_phy_is_fixed_link(port_dn)) {
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1050
1051
1052
1053
1054
  		/* 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 ...
1055
1056
  			netdev_err(slave_dev, "failed to register fixed PHY: %d
  ", ret);
9697f1cde   Florian Fainelli   net: dsa: propaga...
1057
  			return ret;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1058
  		}
ce31b31c6   Florian Fainelli   net: dsa: allow u...
1059
  		phy_is_fixed = true;
0d8f3c671   Johan Hovold   net: dsa: slave: ...
1060
  		phy_dn = of_node_get(port_dn);
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1061
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
1062
1063
  	if (ds->ops->get_phy_flags)
  		phy_flags = ds->ops->get_phy_flags(ds, p->port);
6819563e6   Florian Fainelli   net: dsa: allow s...
1064

cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1065
  	if (phy_dn) {
d25b8e742   Russell King   net: dsa: better ...
1066
  		int phy_id = of_mdio_parse_addr(&slave_dev->dev, phy_dn);
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1067
1068
1069
1070
1071
  		/* 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 ...
1072
1073
1074
1075
1076
1077
  		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: ...
1078
  				of_node_put(phy_dn);
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1079
  				return ret;
d25b8e742   Russell King   net: dsa: better ...
1080
  			}
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1081
1082
1083
1084
1085
1086
  		} 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: ...
1087
1088
  
  		of_node_put(phy_dn);
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1089
  	}
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1090

ce31b31c6   Florian Fainelli   net: dsa: allow u...
1091
1092
  	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...
1093
1094
1095
  	/* We could not connect to a designated PHY, so use the switch internal
  	 * MDIO bus instead
  	 */
b31f65fb4   Andrew Lunn   net: dsa: slave: ...
1096
  	if (!p->phy) {
cd28a1a9b   Florian Fainelli   net: dsa: fully d...
1097
  		ret = dsa_slave_phy_connect(p, slave_dev, p->port);
d25b8e742   Russell King   net: dsa: better ...
1098
1099
1100
  		if (ret) {
  			netdev_err(slave_dev, "failed to connect to port %d: %d
  ", p->port, ret);
881eadabe   Johan Hovold   net: dsa: slave: ...
1101
1102
  			if (phy_is_fixed)
  				of_phy_deregister_fixed_link(port_dn);
c305c1651   Florian Fainelli   net: dsa: move PH...
1103
  			return ret;
d25b8e742   Russell King   net: dsa: better ...
1104
  		}
b31f65fb4   Andrew Lunn   net: dsa: slave: ...
1105
  	}
9697f1cde   Florian Fainelli   net: dsa: propaga...
1106

2220943a2   Andrew Lunn   phy: Centralise p...
1107
  	phy_attached_info(p->phy);
9697f1cde   Florian Fainelli   net: dsa: propaga...
1108
  	return 0;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1109
  }
448b4482c   Andrew Lunn   net: dsa: Add loc...
1110
1111
1112
1113
1114
1115
1116
1117
  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...
1118
1119
1120
  int dsa_slave_suspend(struct net_device *slave_dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(slave_dev);
9f42bc4f9   Florian Fainelli   net: dsa: Bring b...
1121
  	netif_device_detach(slave_dev);
244625491   Florian Fainelli   net: dsa: allow s...
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
  	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);
  
  	netif_device_attach(slave_dev);
  
  	if (p->phy) {
  		phy_resume(p->phy);
  		phy_start(p->phy);
  	}
  
  	return 0;
  }
d87d6f44d   Guenter Roeck   net: dsa: Ensure ...
1146
  int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
83c0afaec   Andrew Lunn   net: dsa: Add new...
1147
  		     int port, const char *name)
91da11f87   Lennert Buytenhek   net: Distributed ...
1148
  {
badf3ada6   Florian Fainelli   net: dsa: Provide...
1149
  	struct dsa_switch_tree *dst = ds->dst;
83c0afaec   Andrew Lunn   net: dsa: Add new...
1150
  	struct net_device *master;
91da11f87   Lennert Buytenhek   net: Distributed ...
1151
1152
1153
  	struct net_device *slave_dev;
  	struct dsa_slave_priv *p;
  	int ret;
83c0afaec   Andrew Lunn   net: dsa: Add new...
1154
1155
1156
  	master = ds->dst->master_netdev;
  	if (ds->master_netdev)
  		master = ds->master_netdev;
c835a6773   Tom Gundersen   net: set name_ass...
1157
1158
  	slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name,
  				 NET_NAME_UNKNOWN, ether_setup);
91da11f87   Lennert Buytenhek   net: Distributed ...
1159
  	if (slave_dev == NULL)
d87d6f44d   Guenter Roeck   net: dsa: Ensure ...
1160
  		return -ENOMEM;
91da11f87   Lennert Buytenhek   net: Distributed ...
1161
1162
  
  	slave_dev->features = master->vlan_features;
7ad24ea4b   Wilfried Klaebe   net: get rid of S...
1163
  	slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
2fcc80058   Bjørn Mork   net: dsa: inherit...
1164
  	eth_hw_addr_inherit(slave_dev, master);
0a5f107b6   Phil Sutter   net: dsa: convert...
1165
  	slave_dev->priv_flags |= IFF_NO_QUEUE;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
1166
  	slave_dev->netdev_ops = &dsa_slave_netdev_ops;
9d47c0a2d   Jiri Pirko   switchdev: s/swde...
1167
  	slave_dev->switchdev_ops = &dsa_slave_switchdev_ops;
f37db85d0   Florian Fainelli   net: dsa: Set a "...
1168
  	SET_NETDEV_DEVTYPE(slave_dev, &dsa_type);
d442ad4ab   Stephen Hemminger   dsa: convert to n...
1169

448b4482c   Andrew Lunn   net: dsa: Add loc...
1170
1171
  	netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one,
  				 NULL);
5075314e4   Alexander Duyck   dsa: Split ops up...
1172
  	SET_NETDEV_DEV(slave_dev, parent);
189b0d93e   Andrew Lunn   net: dsa: Move po...
1173
  	slave_dev->dev.of_node = ds->ports[port].dn;
5075314e4   Alexander Duyck   dsa: Split ops up...
1174
1175
1176
  	slave_dev->vlan_features = master->vlan_features;
  
  	p = netdev_priv(slave_dev);
5075314e4   Alexander Duyck   dsa: Split ops up...
1177
1178
  	p->parent = ds;
  	p->port = port;
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
1179
  	p->xmit = dst->tag_ops->xmit;
d442ad4ab   Stephen Hemminger   dsa: convert to n...
1180

0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1181
1182
1183
  	p->old_pause = -1;
  	p->old_link = -1;
  	p->old_duplex = -1;
c8b098086   Andrew Lunn   net: dsa: Add a p...
1184
  	ds->ports[port].netdev = slave_dev;
91da11f87   Lennert Buytenhek   net: Distributed ...
1185
1186
  	ret = register_netdev(slave_dev);
  	if (ret) {
a2ae6007a   Joe Perches   dsa: Use netdev_<...
1187
1188
1189
  		netdev_err(master, "error %d registering interface %s
  ",
  			   ret, slave_dev->name);
c8b098086   Andrew Lunn   net: dsa: Add a p...
1190
  		ds->ports[port].netdev = NULL;
91da11f87   Lennert Buytenhek   net: Distributed ...
1191
  		free_netdev(slave_dev);
d87d6f44d   Guenter Roeck   net: dsa: Ensure ...
1192
  		return ret;
91da11f87   Lennert Buytenhek   net: Distributed ...
1193
1194
1195
  	}
  
  	netif_carrier_off(slave_dev);
0071f56e4   Andrew Lunn   dsa: Register net...
1196
1197
1198
1199
  	ret = dsa_slave_phy_setup(p, slave_dev);
  	if (ret) {
  		netdev_err(master, "error %d setting up slave phy
  ", ret);
73dcb5565   Florian Fainelli   net: dsa: Unregis...
1200
  		unregister_netdev(slave_dev);
0071f56e4   Andrew Lunn   dsa: Register net...
1201
1202
1203
  		free_netdev(slave_dev);
  		return ret;
  	}
d87d6f44d   Guenter Roeck   net: dsa: Ensure ...
1204
  	return 0;
91da11f87   Lennert Buytenhek   net: Distributed ...
1205
  }
b73adef67   Florian Fainelli   net: dsa: integra...
1206

cda5c15b2   Neil Armstrong   net: dsa: move ds...
1207
1208
1209
  void dsa_slave_destroy(struct net_device *slave_dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(slave_dev);
881eadabe   Johan Hovold   net: dsa: slave: ...
1210
1211
1212
1213
  	struct dsa_switch *ds = p->parent;
  	struct device_node *port_dn;
  
  	port_dn = ds->ports[p->port].dn;
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1214
1215
  
  	netif_carrier_off(slave_dev);
881eadabe   Johan Hovold   net: dsa: slave: ...
1216
  	if (p->phy) {
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1217
  		phy_disconnect(p->phy);
881eadabe   Johan Hovold   net: dsa: slave: ...
1218
1219
1220
1221
  
  		if (of_phy_is_fixed_link(port_dn))
  			of_phy_deregister_fixed_link(port_dn);
  	}
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1222
1223
1224
  	unregister_netdev(slave_dev);
  	free_netdev(slave_dev);
  }
b73adef67   Florian Fainelli   net: dsa: integra...
1225
1226
1227
1228
  static bool dsa_slave_dev_check(struct net_device *dev)
  {
  	return dev->netdev_ops == &dsa_slave_netdev_ops;
  }
6debb68a2   Vivien Didelot   net: dsa: refine ...
1229
1230
  static int dsa_slave_port_upper_event(struct net_device *dev,
  				      unsigned long event, void *ptr)
b73adef67   Florian Fainelli   net: dsa: integra...
1231
  {
6debb68a2   Vivien Didelot   net: dsa: refine ...
1232
1233
  	struct netdev_notifier_changeupper_info *info = ptr;
  	struct net_device *upper = info->upper_dev;
b73adef67   Florian Fainelli   net: dsa: integra...
1234
  	int err = 0;
6debb68a2   Vivien Didelot   net: dsa: refine ...
1235
1236
1237
1238
1239
1240
1241
1242
  	switch (event) {
  	case NETDEV_CHANGEUPPER:
  		if (netif_is_bridge_master(upper)) {
  			if (info->linking)
  				err = dsa_slave_bridge_port_join(dev, upper);
  			else
  				dsa_slave_bridge_port_leave(dev);
  		}
b73adef67   Florian Fainelli   net: dsa: integra...
1243

6debb68a2   Vivien Didelot   net: dsa: refine ...
1244
1245
1246
1247
  		break;
  	}
  
  	return notifier_from_errno(err);
b73adef67   Florian Fainelli   net: dsa: integra...
1248
  }
6debb68a2   Vivien Didelot   net: dsa: refine ...
1249
1250
  static int dsa_slave_port_event(struct net_device *dev, unsigned long event,
  				void *ptr)
b73adef67   Florian Fainelli   net: dsa: integra...
1251
  {
b73adef67   Florian Fainelli   net: dsa: integra...
1252
1253
  	switch (event) {
  	case NETDEV_CHANGEUPPER:
6debb68a2   Vivien Didelot   net: dsa: refine ...
1254
1255
  		return dsa_slave_port_upper_event(dev, event, ptr);
  	}
b73adef67   Florian Fainelli   net: dsa: integra...
1256

6debb68a2   Vivien Didelot   net: dsa: refine ...
1257
1258
  	return NOTIFY_DONE;
  }
b73adef67   Florian Fainelli   net: dsa: integra...
1259

6debb68a2   Vivien Didelot   net: dsa: refine ...
1260
1261
1262
1263
1264
1265
1266
  int dsa_slave_netdevice_event(struct notifier_block *unused,
  			      unsigned long event, void *ptr)
  {
  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
  
  	if (dsa_slave_dev_check(dev))
  		return dsa_slave_port_event(dev, event, ptr);
b73adef67   Florian Fainelli   net: dsa: integra...
1267

b73adef67   Florian Fainelli   net: dsa: integra...
1268
1269
  	return NOTIFY_DONE;
  }