xref: /openbmc/linux/net/dsa/slave.c (revision ba14d9eb1999cad5b810f1fd97d1cb2d3f00869e)
191da11f8SLennert Buytenhek /*
291da11f8SLennert Buytenhek  * net/dsa/slave.c - Slave device handling
3e84665c9SLennert Buytenhek  * Copyright (c) 2008-2009 Marvell Semiconductor
491da11f8SLennert Buytenhek  *
591da11f8SLennert Buytenhek  * This program is free software; you can redistribute it and/or modify
691da11f8SLennert Buytenhek  * it under the terms of the GNU General Public License as published by
791da11f8SLennert Buytenhek  * the Free Software Foundation; either version 2 of the License, or
891da11f8SLennert Buytenhek  * (at your option) any later version.
991da11f8SLennert Buytenhek  */
1091da11f8SLennert Buytenhek 
1191da11f8SLennert Buytenhek #include <linux/list.h>
12df02c6ffSLennert Buytenhek #include <linux/etherdevice.h>
13b73adef6SFlorian Fainelli #include <linux/netdevice.h>
1491da11f8SLennert Buytenhek #include <linux/phy.h>
15a2820543SFlorian Fainelli #include <linux/phy_fixed.h>
160d8bcdd3SFlorian Fainelli #include <linux/of_net.h>
170d8bcdd3SFlorian Fainelli #include <linux/of_mdio.h>
18b73adef6SFlorian Fainelli #include <net/rtnetlink.h>
1998237d43SScott Feldman #include <net/switchdev.h>
20b73adef6SFlorian Fainelli #include <linux/if_bridge.h>
2104ff53f9SFlorian Fainelli #include <linux/netpoll.h>
2291da11f8SLennert Buytenhek #include "dsa_priv.h"
2391da11f8SLennert Buytenhek 
2491da11f8SLennert Buytenhek /* slave mii_bus handling ***************************************************/
2591da11f8SLennert Buytenhek static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
2691da11f8SLennert Buytenhek {
2791da11f8SLennert Buytenhek 	struct dsa_switch *ds = bus->priv;
2891da11f8SLennert Buytenhek 
290d8bcdd3SFlorian Fainelli 	if (ds->phys_mii_mask & (1 << addr))
3091da11f8SLennert Buytenhek 		return ds->drv->phy_read(ds, addr, reg);
3191da11f8SLennert Buytenhek 
3291da11f8SLennert Buytenhek 	return 0xffff;
3391da11f8SLennert Buytenhek }
3491da11f8SLennert Buytenhek 
3591da11f8SLennert Buytenhek static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
3691da11f8SLennert Buytenhek {
3791da11f8SLennert Buytenhek 	struct dsa_switch *ds = bus->priv;
3891da11f8SLennert Buytenhek 
390d8bcdd3SFlorian Fainelli 	if (ds->phys_mii_mask & (1 << addr))
4091da11f8SLennert Buytenhek 		return ds->drv->phy_write(ds, addr, reg, val);
4191da11f8SLennert Buytenhek 
4291da11f8SLennert Buytenhek 	return 0;
4391da11f8SLennert Buytenhek }
4491da11f8SLennert Buytenhek 
4591da11f8SLennert Buytenhek void dsa_slave_mii_bus_init(struct dsa_switch *ds)
4691da11f8SLennert Buytenhek {
4791da11f8SLennert Buytenhek 	ds->slave_mii_bus->priv = (void *)ds;
4891da11f8SLennert Buytenhek 	ds->slave_mii_bus->name = "dsa slave smi";
4991da11f8SLennert Buytenhek 	ds->slave_mii_bus->read = dsa_slave_phy_read;
5091da11f8SLennert Buytenhek 	ds->slave_mii_bus->write = dsa_slave_phy_write;
51f490be04SFlorian Fainelli 	snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d:%.2x",
52f490be04SFlorian Fainelli 			ds->index, ds->pd->sw_addr);
53b4d2394dSAlexander Duyck 	ds->slave_mii_bus->parent = ds->master_dev;
5424df8986SVivien Didelot 	ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
5591da11f8SLennert Buytenhek }
5691da11f8SLennert Buytenhek 
5791da11f8SLennert Buytenhek 
5891da11f8SLennert Buytenhek /* slave device handling ****************************************************/
59abd2be00SNicolas Dichtel static int dsa_slave_get_iflink(const struct net_device *dev)
60c0840801SLennert Buytenhek {
61c0840801SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
62c0840801SLennert Buytenhek 
63abd2be00SNicolas Dichtel 	return p->parent->dst->master_netdev->ifindex;
64c0840801SLennert Buytenhek }
65c0840801SLennert Buytenhek 
66b73adef6SFlorian Fainelli static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p)
67b73adef6SFlorian Fainelli {
68b73adef6SFlorian Fainelli 	return !!p->bridge_dev;
69b73adef6SFlorian Fainelli }
70b73adef6SFlorian Fainelli 
7191da11f8SLennert Buytenhek static int dsa_slave_open(struct net_device *dev)
7291da11f8SLennert Buytenhek {
73df02c6ffSLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
74e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
75b2f2af21SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
76b73adef6SFlorian Fainelli 	u8 stp_state = dsa_port_is_bridged(p) ?
77b73adef6SFlorian Fainelli 			BR_STATE_BLOCKING : BR_STATE_FORWARDING;
78df02c6ffSLennert Buytenhek 	int err;
79df02c6ffSLennert Buytenhek 
80df02c6ffSLennert Buytenhek 	if (!(master->flags & IFF_UP))
81df02c6ffSLennert Buytenhek 		return -ENETDOWN;
82df02c6ffSLennert Buytenhek 
838feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) {
84a748ee24SJiri Pirko 		err = dev_uc_add(master, dev->dev_addr);
85df02c6ffSLennert Buytenhek 		if (err < 0)
86df02c6ffSLennert Buytenhek 			goto out;
87df02c6ffSLennert Buytenhek 	}
88df02c6ffSLennert Buytenhek 
89df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_ALLMULTI) {
90df02c6ffSLennert Buytenhek 		err = dev_set_allmulti(master, 1);
91df02c6ffSLennert Buytenhek 		if (err < 0)
92df02c6ffSLennert Buytenhek 			goto del_unicast;
93df02c6ffSLennert Buytenhek 	}
94df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_PROMISC) {
95df02c6ffSLennert Buytenhek 		err = dev_set_promiscuity(master, 1);
96df02c6ffSLennert Buytenhek 		if (err < 0)
97df02c6ffSLennert Buytenhek 			goto clear_allmulti;
98df02c6ffSLennert Buytenhek 	}
99df02c6ffSLennert Buytenhek 
100b2f2af21SFlorian Fainelli 	if (ds->drv->port_enable) {
101b2f2af21SFlorian Fainelli 		err = ds->drv->port_enable(ds, p->port, p->phy);
102b2f2af21SFlorian Fainelli 		if (err)
103b2f2af21SFlorian Fainelli 			goto clear_promisc;
104b2f2af21SFlorian Fainelli 	}
105b2f2af21SFlorian Fainelli 
106b73adef6SFlorian Fainelli 	if (ds->drv->port_stp_update)
107b73adef6SFlorian Fainelli 		ds->drv->port_stp_update(ds, p->port, stp_state);
108b73adef6SFlorian Fainelli 
109f7f1de51SFlorian Fainelli 	if (p->phy)
110f7f1de51SFlorian Fainelli 		phy_start(p->phy);
111f7f1de51SFlorian Fainelli 
11291da11f8SLennert Buytenhek 	return 0;
113df02c6ffSLennert Buytenhek 
114b2f2af21SFlorian Fainelli clear_promisc:
115b2f2af21SFlorian Fainelli 	if (dev->flags & IFF_PROMISC)
1164fdeddfeSGilad Ben-Yossef 		dev_set_promiscuity(master, -1);
117df02c6ffSLennert Buytenhek clear_allmulti:
118df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_ALLMULTI)
119df02c6ffSLennert Buytenhek 		dev_set_allmulti(master, -1);
120df02c6ffSLennert Buytenhek del_unicast:
1218feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
122a748ee24SJiri Pirko 		dev_uc_del(master, dev->dev_addr);
123df02c6ffSLennert Buytenhek out:
124df02c6ffSLennert Buytenhek 	return err;
12591da11f8SLennert Buytenhek }
12691da11f8SLennert Buytenhek 
12791da11f8SLennert Buytenhek static int dsa_slave_close(struct net_device *dev)
12891da11f8SLennert Buytenhek {
129df02c6ffSLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
130e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
131b2f2af21SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
132df02c6ffSLennert Buytenhek 
133f7f1de51SFlorian Fainelli 	if (p->phy)
134f7f1de51SFlorian Fainelli 		phy_stop(p->phy);
135f7f1de51SFlorian Fainelli 
136df02c6ffSLennert Buytenhek 	dev_mc_unsync(master, dev);
137a748ee24SJiri Pirko 	dev_uc_unsync(master, dev);
138df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_ALLMULTI)
139df02c6ffSLennert Buytenhek 		dev_set_allmulti(master, -1);
140df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_PROMISC)
141df02c6ffSLennert Buytenhek 		dev_set_promiscuity(master, -1);
142df02c6ffSLennert Buytenhek 
1438feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
144a748ee24SJiri Pirko 		dev_uc_del(master, dev->dev_addr);
145df02c6ffSLennert Buytenhek 
146b2f2af21SFlorian Fainelli 	if (ds->drv->port_disable)
147b2f2af21SFlorian Fainelli 		ds->drv->port_disable(ds, p->port, p->phy);
148b2f2af21SFlorian Fainelli 
149b73adef6SFlorian Fainelli 	if (ds->drv->port_stp_update)
150b73adef6SFlorian Fainelli 		ds->drv->port_stp_update(ds, p->port, BR_STATE_DISABLED);
151b73adef6SFlorian Fainelli 
15291da11f8SLennert Buytenhek 	return 0;
15391da11f8SLennert Buytenhek }
15491da11f8SLennert Buytenhek 
15591da11f8SLennert Buytenhek static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
15691da11f8SLennert Buytenhek {
15791da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
158e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
15991da11f8SLennert Buytenhek 
16091da11f8SLennert Buytenhek 	if (change & IFF_ALLMULTI)
16191da11f8SLennert Buytenhek 		dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
16291da11f8SLennert Buytenhek 	if (change & IFF_PROMISC)
16391da11f8SLennert Buytenhek 		dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1);
16491da11f8SLennert Buytenhek }
16591da11f8SLennert Buytenhek 
16691da11f8SLennert Buytenhek static void dsa_slave_set_rx_mode(struct net_device *dev)
16791da11f8SLennert Buytenhek {
16891da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
169e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
17091da11f8SLennert Buytenhek 
17191da11f8SLennert Buytenhek 	dev_mc_sync(master, dev);
172a748ee24SJiri Pirko 	dev_uc_sync(master, dev);
17391da11f8SLennert Buytenhek }
17491da11f8SLennert Buytenhek 
175df02c6ffSLennert Buytenhek static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
17691da11f8SLennert Buytenhek {
177df02c6ffSLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
178e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
179df02c6ffSLennert Buytenhek 	struct sockaddr *addr = a;
180df02c6ffSLennert Buytenhek 	int err;
181df02c6ffSLennert Buytenhek 
182df02c6ffSLennert Buytenhek 	if (!is_valid_ether_addr(addr->sa_data))
183df02c6ffSLennert Buytenhek 		return -EADDRNOTAVAIL;
184df02c6ffSLennert Buytenhek 
185df02c6ffSLennert Buytenhek 	if (!(dev->flags & IFF_UP))
186df02c6ffSLennert Buytenhek 		goto out;
187df02c6ffSLennert Buytenhek 
1888feedbb4SJoe Perches 	if (!ether_addr_equal(addr->sa_data, master->dev_addr)) {
189a748ee24SJiri Pirko 		err = dev_uc_add(master, addr->sa_data);
190df02c6ffSLennert Buytenhek 		if (err < 0)
191df02c6ffSLennert Buytenhek 			return err;
192df02c6ffSLennert Buytenhek 	}
193df02c6ffSLennert Buytenhek 
1948feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
195a748ee24SJiri Pirko 		dev_uc_del(master, dev->dev_addr);
196df02c6ffSLennert Buytenhek 
197df02c6ffSLennert Buytenhek out:
198d08f161aSJoe Perches 	ether_addr_copy(dev->dev_addr, addr->sa_data);
19991da11f8SLennert Buytenhek 
20091da11f8SLennert Buytenhek 	return 0;
20191da11f8SLennert Buytenhek }
20291da11f8SLennert Buytenhek 
203*ba14d9ebSVivien Didelot static int dsa_slave_port_fdb_add(struct net_device *dev,
204*ba14d9ebSVivien Didelot 				  struct switchdev_obj *obj)
205cdf09697SDavid S. Miller {
206*ba14d9ebSVivien Didelot 	struct switchdev_obj_fdb *fdb = &obj->u.fdb;
207cdf09697SDavid S. Miller 	struct dsa_slave_priv *p = netdev_priv(dev);
208cdf09697SDavid S. Miller 	struct dsa_switch *ds = p->parent;
209cdf09697SDavid S. Miller 	int ret = -EOPNOTSUPP;
210cdf09697SDavid S. Miller 
211*ba14d9ebSVivien Didelot 	if (obj->trans == SWITCHDEV_TRANS_PREPARE)
212*ba14d9ebSVivien Didelot 		ret = ds->drv->port_fdb_add ? 0 : -EOPNOTSUPP;
213*ba14d9ebSVivien Didelot 	else if (obj->trans == SWITCHDEV_TRANS_COMMIT)
214*ba14d9ebSVivien Didelot 		ret = ds->drv->port_fdb_add(ds, p->port, fdb->addr, fdb->vid);
215cdf09697SDavid S. Miller 
216cdf09697SDavid S. Miller 	return ret;
217cdf09697SDavid S. Miller }
218cdf09697SDavid S. Miller 
219*ba14d9ebSVivien Didelot static int dsa_slave_port_fdb_del(struct net_device *dev,
220*ba14d9ebSVivien Didelot 				  struct switchdev_obj *obj)
221cdf09697SDavid S. Miller {
222*ba14d9ebSVivien Didelot 	struct switchdev_obj_fdb *fdb = &obj->u.fdb;
223cdf09697SDavid S. Miller 	struct dsa_slave_priv *p = netdev_priv(dev);
224cdf09697SDavid S. Miller 	struct dsa_switch *ds = p->parent;
225cdf09697SDavid S. Miller 	int ret = -EOPNOTSUPP;
226cdf09697SDavid S. Miller 
2272a778e1bSVivien Didelot 	if (ds->drv->port_fdb_del)
228*ba14d9ebSVivien Didelot 		ret = ds->drv->port_fdb_del(ds, p->port, fdb->addr, fdb->vid);
229cdf09697SDavid S. Miller 
230cdf09697SDavid S. Miller 	return ret;
231cdf09697SDavid S. Miller }
232cdf09697SDavid S. Miller 
233*ba14d9ebSVivien Didelot static int dsa_slave_port_fdb_dump(struct net_device *dev,
234*ba14d9ebSVivien Didelot 				   struct switchdev_obj *obj)
235cdf09697SDavid S. Miller {
236cdf09697SDavid S. Miller 	struct dsa_slave_priv *p = netdev_priv(dev);
237cdf09697SDavid S. Miller 	struct dsa_switch *ds = p->parent;
238cdf09697SDavid S. Miller 	unsigned char addr[ETH_ALEN] = { 0 };
2392a778e1bSVivien Didelot 	u16 vid = 0;
240cdf09697SDavid S. Miller 	int ret;
241cdf09697SDavid S. Miller 
2422a778e1bSVivien Didelot 	if (!ds->drv->port_fdb_getnext)
243cdf09697SDavid S. Miller 		return -EOPNOTSUPP;
244cdf09697SDavid S. Miller 
245*ba14d9ebSVivien Didelot 	for (;;) {
246cdf09697SDavid S. Miller 		bool is_static;
247cdf09697SDavid S. Miller 
2482a778e1bSVivien Didelot 		ret = ds->drv->port_fdb_getnext(ds, p->port, addr, &vid,
2492a778e1bSVivien Didelot 						&is_static);
250cdf09697SDavid S. Miller 		if (ret < 0)
251cdf09697SDavid S. Miller 			break;
252cdf09697SDavid S. Miller 
253*ba14d9ebSVivien Didelot 		obj->u.fdb.addr = addr;
254*ba14d9ebSVivien Didelot 		obj->u.fdb.vid = vid;
255*ba14d9ebSVivien Didelot 		obj->u.fdb.ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
256cdf09697SDavid S. Miller 
257*ba14d9ebSVivien Didelot 		ret = obj->cb(dev, obj);
258cdf09697SDavid S. Miller 		if (ret < 0)
259cdf09697SDavid S. Miller 			break;
260cdf09697SDavid S. Miller 	}
261cdf09697SDavid S. Miller 
262*ba14d9ebSVivien Didelot 	return ret == -ENOENT ? 0 : ret;
263cdf09697SDavid S. Miller }
264cdf09697SDavid S. Miller 
26591da11f8SLennert Buytenhek static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
26691da11f8SLennert Buytenhek {
26791da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
26891da11f8SLennert Buytenhek 
26991da11f8SLennert Buytenhek 	if (p->phy != NULL)
27028b04113SRichard Cochran 		return phy_mii_ioctl(p->phy, ifr, cmd);
27191da11f8SLennert Buytenhek 
27291da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
27391da11f8SLennert Buytenhek }
27491da11f8SLennert Buytenhek 
275b73adef6SFlorian Fainelli /* Return a bitmask of all ports being currently bridged within a given bridge
276b73adef6SFlorian Fainelli  * device. Note that on leave, the mask will still return the bitmask of ports
277b73adef6SFlorian Fainelli  * currently bridged, prior to port removal, and this is exactly what we want.
278b73adef6SFlorian Fainelli  */
279b73adef6SFlorian Fainelli static u32 dsa_slave_br_port_mask(struct dsa_switch *ds,
280b73adef6SFlorian Fainelli 				  struct net_device *bridge)
281b73adef6SFlorian Fainelli {
282b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p;
283b73adef6SFlorian Fainelli 	unsigned int port;
284b73adef6SFlorian Fainelli 	u32 mask = 0;
285b73adef6SFlorian Fainelli 
286b73adef6SFlorian Fainelli 	for (port = 0; port < DSA_MAX_PORTS; port++) {
287d79d2107SGuenter Roeck 		if (!dsa_is_port_initialized(ds, port))
288b73adef6SFlorian Fainelli 			continue;
289b73adef6SFlorian Fainelli 
290b73adef6SFlorian Fainelli 		p = netdev_priv(ds->ports[port]);
291b73adef6SFlorian Fainelli 
292b73adef6SFlorian Fainelli 		if (ds->ports[port]->priv_flags & IFF_BRIDGE_PORT &&
293b73adef6SFlorian Fainelli 		    p->bridge_dev == bridge)
294b73adef6SFlorian Fainelli 			mask |= 1 << port;
295b73adef6SFlorian Fainelli 	}
296b73adef6SFlorian Fainelli 
297b73adef6SFlorian Fainelli 	return mask;
298b73adef6SFlorian Fainelli }
299b73adef6SFlorian Fainelli 
300b73adef6SFlorian Fainelli static int dsa_slave_stp_update(struct net_device *dev, u8 state)
301b73adef6SFlorian Fainelli {
302b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
303b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
304b73adef6SFlorian Fainelli 	int ret = -EOPNOTSUPP;
305b73adef6SFlorian Fainelli 
306b73adef6SFlorian Fainelli 	if (ds->drv->port_stp_update)
307b73adef6SFlorian Fainelli 		ret = ds->drv->port_stp_update(ds, p->port, state);
308b73adef6SFlorian Fainelli 
309b73adef6SFlorian Fainelli 	return ret;
310b73adef6SFlorian Fainelli }
311b73adef6SFlorian Fainelli 
31235636062SScott Feldman static int dsa_slave_port_attr_set(struct net_device *dev,
31335636062SScott Feldman 				   struct switchdev_attr *attr)
31435636062SScott Feldman {
31535636062SScott Feldman 	int ret = 0;
31635636062SScott Feldman 
31735636062SScott Feldman 	switch (attr->id) {
31835636062SScott Feldman 	case SWITCHDEV_ATTR_PORT_STP_STATE:
31935636062SScott Feldman 		if (attr->trans == SWITCHDEV_TRANS_COMMIT)
32042275bd8SScott Feldman 			ret = dsa_slave_stp_update(dev, attr->u.stp_state);
32135636062SScott Feldman 		break;
32235636062SScott Feldman 	default:
32335636062SScott Feldman 		ret = -EOPNOTSUPP;
32435636062SScott Feldman 		break;
32535636062SScott Feldman 	}
32635636062SScott Feldman 
32735636062SScott Feldman 	return ret;
32835636062SScott Feldman }
32935636062SScott Feldman 
330*ba14d9ebSVivien Didelot static int dsa_slave_port_obj_add(struct net_device *dev,
331*ba14d9ebSVivien Didelot 				  struct switchdev_obj *obj)
332*ba14d9ebSVivien Didelot {
333*ba14d9ebSVivien Didelot 	int err;
334*ba14d9ebSVivien Didelot 
335*ba14d9ebSVivien Didelot 	/* For the prepare phase, ensure the full set of changes is feasable in
336*ba14d9ebSVivien Didelot 	 * one go in order to signal a failure properly. If an operation is not
337*ba14d9ebSVivien Didelot 	 * supported, return -EOPNOTSUPP.
338*ba14d9ebSVivien Didelot 	 */
339*ba14d9ebSVivien Didelot 
340*ba14d9ebSVivien Didelot 	switch (obj->id) {
341*ba14d9ebSVivien Didelot 	case SWITCHDEV_OBJ_PORT_FDB:
342*ba14d9ebSVivien Didelot 		err = dsa_slave_port_fdb_add(dev, obj);
343*ba14d9ebSVivien Didelot 		break;
344*ba14d9ebSVivien Didelot 	default:
345*ba14d9ebSVivien Didelot 		err = -EOPNOTSUPP;
346*ba14d9ebSVivien Didelot 		break;
347*ba14d9ebSVivien Didelot 	}
348*ba14d9ebSVivien Didelot 
349*ba14d9ebSVivien Didelot 	return err;
350*ba14d9ebSVivien Didelot }
351*ba14d9ebSVivien Didelot 
352*ba14d9ebSVivien Didelot static int dsa_slave_port_obj_del(struct net_device *dev,
353*ba14d9ebSVivien Didelot 				  struct switchdev_obj *obj)
354*ba14d9ebSVivien Didelot {
355*ba14d9ebSVivien Didelot 	int err;
356*ba14d9ebSVivien Didelot 
357*ba14d9ebSVivien Didelot 	switch (obj->id) {
358*ba14d9ebSVivien Didelot 	case SWITCHDEV_OBJ_PORT_FDB:
359*ba14d9ebSVivien Didelot 		err = dsa_slave_port_fdb_del(dev, obj);
360*ba14d9ebSVivien Didelot 		break;
361*ba14d9ebSVivien Didelot 	default:
362*ba14d9ebSVivien Didelot 		err = -EOPNOTSUPP;
363*ba14d9ebSVivien Didelot 		break;
364*ba14d9ebSVivien Didelot 	}
365*ba14d9ebSVivien Didelot 
366*ba14d9ebSVivien Didelot 	return err;
367*ba14d9ebSVivien Didelot }
368*ba14d9ebSVivien Didelot 
369*ba14d9ebSVivien Didelot static int dsa_slave_port_obj_dump(struct net_device *dev,
370*ba14d9ebSVivien Didelot 				   struct switchdev_obj *obj)
371*ba14d9ebSVivien Didelot {
372*ba14d9ebSVivien Didelot 	int err;
373*ba14d9ebSVivien Didelot 
374*ba14d9ebSVivien Didelot 	switch (obj->id) {
375*ba14d9ebSVivien Didelot 	case SWITCHDEV_OBJ_PORT_FDB:
376*ba14d9ebSVivien Didelot 		err = dsa_slave_port_fdb_dump(dev, obj);
377*ba14d9ebSVivien Didelot 		break;
378*ba14d9ebSVivien Didelot 	default:
379*ba14d9ebSVivien Didelot 		err = -EOPNOTSUPP;
380*ba14d9ebSVivien Didelot 		break;
381*ba14d9ebSVivien Didelot 	}
382*ba14d9ebSVivien Didelot 
383*ba14d9ebSVivien Didelot 	return err;
384*ba14d9ebSVivien Didelot }
385*ba14d9ebSVivien Didelot 
386b73adef6SFlorian Fainelli static int dsa_slave_bridge_port_join(struct net_device *dev,
387b73adef6SFlorian Fainelli 				      struct net_device *br)
388b73adef6SFlorian Fainelli {
389b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
390b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
391b73adef6SFlorian Fainelli 	int ret = -EOPNOTSUPP;
392b73adef6SFlorian Fainelli 
393b73adef6SFlorian Fainelli 	p->bridge_dev = br;
394b73adef6SFlorian Fainelli 
395b73adef6SFlorian Fainelli 	if (ds->drv->port_join_bridge)
396b73adef6SFlorian Fainelli 		ret = ds->drv->port_join_bridge(ds, p->port,
397b73adef6SFlorian Fainelli 						dsa_slave_br_port_mask(ds, br));
398b73adef6SFlorian Fainelli 
399b73adef6SFlorian Fainelli 	return ret;
400b73adef6SFlorian Fainelli }
401b73adef6SFlorian Fainelli 
402b73adef6SFlorian Fainelli static int dsa_slave_bridge_port_leave(struct net_device *dev)
403b73adef6SFlorian Fainelli {
404b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
405b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
406b73adef6SFlorian Fainelli 	int ret = -EOPNOTSUPP;
407b73adef6SFlorian Fainelli 
408b73adef6SFlorian Fainelli 
409b73adef6SFlorian Fainelli 	if (ds->drv->port_leave_bridge)
410b73adef6SFlorian Fainelli 		ret = ds->drv->port_leave_bridge(ds, p->port,
411b73adef6SFlorian Fainelli 						 dsa_slave_br_port_mask(ds, p->bridge_dev));
412b73adef6SFlorian Fainelli 
413b73adef6SFlorian Fainelli 	p->bridge_dev = NULL;
414b73adef6SFlorian Fainelli 
415b73adef6SFlorian Fainelli 	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
416b73adef6SFlorian Fainelli 	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
417b73adef6SFlorian Fainelli 	 */
418b73adef6SFlorian Fainelli 	dsa_slave_stp_update(dev, BR_STATE_FORWARDING);
419b73adef6SFlorian Fainelli 
420b73adef6SFlorian Fainelli 	return ret;
421b73adef6SFlorian Fainelli }
422b73adef6SFlorian Fainelli 
423f8e20a9fSScott Feldman static int dsa_slave_port_attr_get(struct net_device *dev,
424f8e20a9fSScott Feldman 				   struct switchdev_attr *attr)
425b73adef6SFlorian Fainelli {
426b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
427b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
428b73adef6SFlorian Fainelli 
429f8e20a9fSScott Feldman 	switch (attr->id) {
430f8e20a9fSScott Feldman 	case SWITCHDEV_ATTR_PORT_PARENT_ID:
43142275bd8SScott Feldman 		attr->u.ppid.id_len = sizeof(ds->index);
43242275bd8SScott Feldman 		memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len);
433f8e20a9fSScott Feldman 		break;
434f8e20a9fSScott Feldman 	default:
435f8e20a9fSScott Feldman 		return -EOPNOTSUPP;
436f8e20a9fSScott Feldman 	}
437b73adef6SFlorian Fainelli 
438b73adef6SFlorian Fainelli 	return 0;
439b73adef6SFlorian Fainelli }
440b73adef6SFlorian Fainelli 
44104ff53f9SFlorian Fainelli static inline netdev_tx_t dsa_netpoll_send_skb(struct dsa_slave_priv *p,
44204ff53f9SFlorian Fainelli 					       struct sk_buff *skb)
44304ff53f9SFlorian Fainelli {
44404ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
44504ff53f9SFlorian Fainelli 	if (p->netpoll)
44604ff53f9SFlorian Fainelli 		netpoll_send_skb(p->netpoll, skb);
44704ff53f9SFlorian Fainelli #else
44804ff53f9SFlorian Fainelli 	BUG();
44904ff53f9SFlorian Fainelli #endif
45004ff53f9SFlorian Fainelli 	return NETDEV_TX_OK;
45104ff53f9SFlorian Fainelli }
45204ff53f9SFlorian Fainelli 
4533e8a72d1SFlorian Fainelli static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
4543e8a72d1SFlorian Fainelli {
4553e8a72d1SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
4564ed70ce9SFlorian Fainelli 	struct sk_buff *nskb;
4573e8a72d1SFlorian Fainelli 
4584ed70ce9SFlorian Fainelli 	dev->stats.tx_packets++;
4594ed70ce9SFlorian Fainelli 	dev->stats.tx_bytes += skb->len;
4603e8a72d1SFlorian Fainelli 
4614ed70ce9SFlorian Fainelli 	/* Transmit function may have to reallocate the original SKB */
4624ed70ce9SFlorian Fainelli 	nskb = p->xmit(skb, dev);
4634ed70ce9SFlorian Fainelli 	if (!nskb)
4644ed70ce9SFlorian Fainelli 		return NETDEV_TX_OK;
4655aed85ceSFlorian Fainelli 
46604ff53f9SFlorian Fainelli 	/* SKB for netpoll still need to be mangled with the protocol-specific
46704ff53f9SFlorian Fainelli 	 * tag to be successfully transmitted
46804ff53f9SFlorian Fainelli 	 */
46904ff53f9SFlorian Fainelli 	if (unlikely(netpoll_tx_running(dev)))
47004ff53f9SFlorian Fainelli 		return dsa_netpoll_send_skb(p, nskb);
47104ff53f9SFlorian Fainelli 
4724ed70ce9SFlorian Fainelli 	/* Queue the SKB for transmission on the parent interface, but
4734ed70ce9SFlorian Fainelli 	 * do not modify its EtherType
4744ed70ce9SFlorian Fainelli 	 */
4754ed70ce9SFlorian Fainelli 	nskb->dev = p->parent->dst->master_netdev;
4764ed70ce9SFlorian Fainelli 	dev_queue_xmit(nskb);
4775aed85ceSFlorian Fainelli 
4785aed85ceSFlorian Fainelli 	return NETDEV_TX_OK;
4795aed85ceSFlorian Fainelli }
4805aed85ceSFlorian Fainelli 
4814ed70ce9SFlorian Fainelli static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb,
4824ed70ce9SFlorian Fainelli 					    struct net_device *dev)
4834ed70ce9SFlorian Fainelli {
4844ed70ce9SFlorian Fainelli 	/* Just return the original SKB */
4854ed70ce9SFlorian Fainelli 	return skb;
4864ed70ce9SFlorian Fainelli }
4874ed70ce9SFlorian Fainelli 
48891da11f8SLennert Buytenhek 
48991da11f8SLennert Buytenhek /* ethtool operations *******************************************************/
49091da11f8SLennert Buytenhek static int
49191da11f8SLennert Buytenhek dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
49291da11f8SLennert Buytenhek {
49391da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
49491da11f8SLennert Buytenhek 	int err;
49591da11f8SLennert Buytenhek 
49691da11f8SLennert Buytenhek 	err = -EOPNOTSUPP;
49791da11f8SLennert Buytenhek 	if (p->phy != NULL) {
49891da11f8SLennert Buytenhek 		err = phy_read_status(p->phy);
49991da11f8SLennert Buytenhek 		if (err == 0)
50091da11f8SLennert Buytenhek 			err = phy_ethtool_gset(p->phy, cmd);
50191da11f8SLennert Buytenhek 	}
50291da11f8SLennert Buytenhek 
50391da11f8SLennert Buytenhek 	return err;
50491da11f8SLennert Buytenhek }
50591da11f8SLennert Buytenhek 
50691da11f8SLennert Buytenhek static int
50791da11f8SLennert Buytenhek dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
50891da11f8SLennert Buytenhek {
50991da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
51091da11f8SLennert Buytenhek 
51191da11f8SLennert Buytenhek 	if (p->phy != NULL)
51291da11f8SLennert Buytenhek 		return phy_ethtool_sset(p->phy, cmd);
51391da11f8SLennert Buytenhek 
51491da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
51591da11f8SLennert Buytenhek }
51691da11f8SLennert Buytenhek 
51791da11f8SLennert Buytenhek static void dsa_slave_get_drvinfo(struct net_device *dev,
51891da11f8SLennert Buytenhek 				  struct ethtool_drvinfo *drvinfo)
51991da11f8SLennert Buytenhek {
5207826d43fSJiri Pirko 	strlcpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver));
5217826d43fSJiri Pirko 	strlcpy(drvinfo->version, dsa_driver_version, sizeof(drvinfo->version));
5227826d43fSJiri Pirko 	strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
5237826d43fSJiri Pirko 	strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
52491da11f8SLennert Buytenhek }
52591da11f8SLennert Buytenhek 
5263d762a0fSGuenter Roeck static int dsa_slave_get_regs_len(struct net_device *dev)
5273d762a0fSGuenter Roeck {
5283d762a0fSGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
5293d762a0fSGuenter Roeck 	struct dsa_switch *ds = p->parent;
5303d762a0fSGuenter Roeck 
5313d762a0fSGuenter Roeck 	if (ds->drv->get_regs_len)
5323d762a0fSGuenter Roeck 		return ds->drv->get_regs_len(ds, p->port);
5333d762a0fSGuenter Roeck 
5343d762a0fSGuenter Roeck 	return -EOPNOTSUPP;
5353d762a0fSGuenter Roeck }
5363d762a0fSGuenter Roeck 
5373d762a0fSGuenter Roeck static void
5383d762a0fSGuenter Roeck dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
5393d762a0fSGuenter Roeck {
5403d762a0fSGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
5413d762a0fSGuenter Roeck 	struct dsa_switch *ds = p->parent;
5423d762a0fSGuenter Roeck 
5433d762a0fSGuenter Roeck 	if (ds->drv->get_regs)
5443d762a0fSGuenter Roeck 		ds->drv->get_regs(ds, p->port, regs, _p);
5453d762a0fSGuenter Roeck }
5463d762a0fSGuenter Roeck 
54791da11f8SLennert Buytenhek static int dsa_slave_nway_reset(struct net_device *dev)
54891da11f8SLennert Buytenhek {
54991da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
55091da11f8SLennert Buytenhek 
55191da11f8SLennert Buytenhek 	if (p->phy != NULL)
55291da11f8SLennert Buytenhek 		return genphy_restart_aneg(p->phy);
55391da11f8SLennert Buytenhek 
55491da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
55591da11f8SLennert Buytenhek }
55691da11f8SLennert Buytenhek 
55791da11f8SLennert Buytenhek static u32 dsa_slave_get_link(struct net_device *dev)
55891da11f8SLennert Buytenhek {
55991da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
56091da11f8SLennert Buytenhek 
56191da11f8SLennert Buytenhek 	if (p->phy != NULL) {
56291da11f8SLennert Buytenhek 		genphy_update_link(p->phy);
56391da11f8SLennert Buytenhek 		return p->phy->link;
56491da11f8SLennert Buytenhek 	}
56591da11f8SLennert Buytenhek 
56691da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
56791da11f8SLennert Buytenhek }
56891da11f8SLennert Buytenhek 
5696793abb4SGuenter Roeck static int dsa_slave_get_eeprom_len(struct net_device *dev)
5706793abb4SGuenter Roeck {
5716793abb4SGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
5726793abb4SGuenter Roeck 	struct dsa_switch *ds = p->parent;
5736793abb4SGuenter Roeck 
5746793abb4SGuenter Roeck 	if (ds->pd->eeprom_len)
5756793abb4SGuenter Roeck 		return ds->pd->eeprom_len;
5766793abb4SGuenter Roeck 
5776793abb4SGuenter Roeck 	if (ds->drv->get_eeprom_len)
5786793abb4SGuenter Roeck 		return ds->drv->get_eeprom_len(ds);
5796793abb4SGuenter Roeck 
5806793abb4SGuenter Roeck 	return 0;
5816793abb4SGuenter Roeck }
5826793abb4SGuenter Roeck 
5836793abb4SGuenter Roeck static int dsa_slave_get_eeprom(struct net_device *dev,
5846793abb4SGuenter Roeck 				struct ethtool_eeprom *eeprom, u8 *data)
5856793abb4SGuenter Roeck {
5866793abb4SGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
5876793abb4SGuenter Roeck 	struct dsa_switch *ds = p->parent;
5886793abb4SGuenter Roeck 
5896793abb4SGuenter Roeck 	if (ds->drv->get_eeprom)
5906793abb4SGuenter Roeck 		return ds->drv->get_eeprom(ds, eeprom, data);
5916793abb4SGuenter Roeck 
5926793abb4SGuenter Roeck 	return -EOPNOTSUPP;
5936793abb4SGuenter Roeck }
5946793abb4SGuenter Roeck 
5956793abb4SGuenter Roeck static int dsa_slave_set_eeprom(struct net_device *dev,
5966793abb4SGuenter Roeck 				struct ethtool_eeprom *eeprom, u8 *data)
5976793abb4SGuenter Roeck {
5986793abb4SGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
5996793abb4SGuenter Roeck 	struct dsa_switch *ds = p->parent;
6006793abb4SGuenter Roeck 
6016793abb4SGuenter Roeck 	if (ds->drv->set_eeprom)
6026793abb4SGuenter Roeck 		return ds->drv->set_eeprom(ds, eeprom, data);
6036793abb4SGuenter Roeck 
6046793abb4SGuenter Roeck 	return -EOPNOTSUPP;
6056793abb4SGuenter Roeck }
6066793abb4SGuenter Roeck 
60791da11f8SLennert Buytenhek static void dsa_slave_get_strings(struct net_device *dev,
60891da11f8SLennert Buytenhek 				  uint32_t stringset, uint8_t *data)
60991da11f8SLennert Buytenhek {
61091da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
61191da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
61291da11f8SLennert Buytenhek 
61391da11f8SLennert Buytenhek 	if (stringset == ETH_SS_STATS) {
61491da11f8SLennert Buytenhek 		int len = ETH_GSTRING_LEN;
61591da11f8SLennert Buytenhek 
61691da11f8SLennert Buytenhek 		strncpy(data, "tx_packets", len);
61791da11f8SLennert Buytenhek 		strncpy(data + len, "tx_bytes", len);
61891da11f8SLennert Buytenhek 		strncpy(data + 2 * len, "rx_packets", len);
61991da11f8SLennert Buytenhek 		strncpy(data + 3 * len, "rx_bytes", len);
62091da11f8SLennert Buytenhek 		if (ds->drv->get_strings != NULL)
62191da11f8SLennert Buytenhek 			ds->drv->get_strings(ds, p->port, data + 4 * len);
62291da11f8SLennert Buytenhek 	}
62391da11f8SLennert Buytenhek }
62491da11f8SLennert Buytenhek 
62591da11f8SLennert Buytenhek static void dsa_slave_get_ethtool_stats(struct net_device *dev,
62691da11f8SLennert Buytenhek 					struct ethtool_stats *stats,
62791da11f8SLennert Buytenhek 					uint64_t *data)
62891da11f8SLennert Buytenhek {
62991da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
63091da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
63191da11f8SLennert Buytenhek 
63291da11f8SLennert Buytenhek 	data[0] = p->dev->stats.tx_packets;
63391da11f8SLennert Buytenhek 	data[1] = p->dev->stats.tx_bytes;
63491da11f8SLennert Buytenhek 	data[2] = p->dev->stats.rx_packets;
63591da11f8SLennert Buytenhek 	data[3] = p->dev->stats.rx_bytes;
63691da11f8SLennert Buytenhek 	if (ds->drv->get_ethtool_stats != NULL)
63791da11f8SLennert Buytenhek 		ds->drv->get_ethtool_stats(ds, p->port, data + 4);
63891da11f8SLennert Buytenhek }
63991da11f8SLennert Buytenhek 
64091da11f8SLennert Buytenhek static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
64191da11f8SLennert Buytenhek {
64291da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
64391da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
64491da11f8SLennert Buytenhek 
64591da11f8SLennert Buytenhek 	if (sset == ETH_SS_STATS) {
64691da11f8SLennert Buytenhek 		int count;
64791da11f8SLennert Buytenhek 
64891da11f8SLennert Buytenhek 		count = 4;
64991da11f8SLennert Buytenhek 		if (ds->drv->get_sset_count != NULL)
65091da11f8SLennert Buytenhek 			count += ds->drv->get_sset_count(ds);
65191da11f8SLennert Buytenhek 
65291da11f8SLennert Buytenhek 		return count;
65391da11f8SLennert Buytenhek 	}
65491da11f8SLennert Buytenhek 
65591da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
65691da11f8SLennert Buytenhek }
65791da11f8SLennert Buytenhek 
65819e57c4eSFlorian Fainelli static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
65919e57c4eSFlorian Fainelli {
66019e57c4eSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
66119e57c4eSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
66219e57c4eSFlorian Fainelli 
66319e57c4eSFlorian Fainelli 	if (ds->drv->get_wol)
66419e57c4eSFlorian Fainelli 		ds->drv->get_wol(ds, p->port, w);
66519e57c4eSFlorian Fainelli }
66619e57c4eSFlorian Fainelli 
66719e57c4eSFlorian Fainelli static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
66819e57c4eSFlorian Fainelli {
66919e57c4eSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
67019e57c4eSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
67119e57c4eSFlorian Fainelli 	int ret = -EOPNOTSUPP;
67219e57c4eSFlorian Fainelli 
67319e57c4eSFlorian Fainelli 	if (ds->drv->set_wol)
67419e57c4eSFlorian Fainelli 		ret = ds->drv->set_wol(ds, p->port, w);
67519e57c4eSFlorian Fainelli 
67619e57c4eSFlorian Fainelli 	return ret;
67719e57c4eSFlorian Fainelli }
67819e57c4eSFlorian Fainelli 
6797905288fSFlorian Fainelli static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
6807905288fSFlorian Fainelli {
6817905288fSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
6827905288fSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
6837905288fSFlorian Fainelli 	int ret;
6847905288fSFlorian Fainelli 
6857905288fSFlorian Fainelli 	if (!ds->drv->set_eee)
6867905288fSFlorian Fainelli 		return -EOPNOTSUPP;
6877905288fSFlorian Fainelli 
6887905288fSFlorian Fainelli 	ret = ds->drv->set_eee(ds, p->port, p->phy, e);
6897905288fSFlorian Fainelli 	if (ret)
6907905288fSFlorian Fainelli 		return ret;
6917905288fSFlorian Fainelli 
6927905288fSFlorian Fainelli 	if (p->phy)
6937905288fSFlorian Fainelli 		ret = phy_ethtool_set_eee(p->phy, e);
6947905288fSFlorian Fainelli 
6957905288fSFlorian Fainelli 	return ret;
6967905288fSFlorian Fainelli }
6977905288fSFlorian Fainelli 
6987905288fSFlorian Fainelli static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
6997905288fSFlorian Fainelli {
7007905288fSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
7017905288fSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
7027905288fSFlorian Fainelli 	int ret;
7037905288fSFlorian Fainelli 
7047905288fSFlorian Fainelli 	if (!ds->drv->get_eee)
7057905288fSFlorian Fainelli 		return -EOPNOTSUPP;
7067905288fSFlorian Fainelli 
7077905288fSFlorian Fainelli 	ret = ds->drv->get_eee(ds, p->port, e);
7087905288fSFlorian Fainelli 	if (ret)
7097905288fSFlorian Fainelli 		return ret;
7107905288fSFlorian Fainelli 
7117905288fSFlorian Fainelli 	if (p->phy)
7127905288fSFlorian Fainelli 		ret = phy_ethtool_get_eee(p->phy, e);
7137905288fSFlorian Fainelli 
7147905288fSFlorian Fainelli 	return ret;
7157905288fSFlorian Fainelli }
7167905288fSFlorian Fainelli 
71704ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
71804ff53f9SFlorian Fainelli static int dsa_slave_netpoll_setup(struct net_device *dev,
71904ff53f9SFlorian Fainelli 				   struct netpoll_info *ni)
72004ff53f9SFlorian Fainelli {
72104ff53f9SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
72204ff53f9SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
72304ff53f9SFlorian Fainelli 	struct net_device *master = ds->dst->master_netdev;
72404ff53f9SFlorian Fainelli 	struct netpoll *netpoll;
72504ff53f9SFlorian Fainelli 	int err = 0;
72604ff53f9SFlorian Fainelli 
72704ff53f9SFlorian Fainelli 	netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
72804ff53f9SFlorian Fainelli 	if (!netpoll)
72904ff53f9SFlorian Fainelli 		return -ENOMEM;
73004ff53f9SFlorian Fainelli 
73104ff53f9SFlorian Fainelli 	err = __netpoll_setup(netpoll, master);
73204ff53f9SFlorian Fainelli 	if (err) {
73304ff53f9SFlorian Fainelli 		kfree(netpoll);
73404ff53f9SFlorian Fainelli 		goto out;
73504ff53f9SFlorian Fainelli 	}
73604ff53f9SFlorian Fainelli 
73704ff53f9SFlorian Fainelli 	p->netpoll = netpoll;
73804ff53f9SFlorian Fainelli out:
73904ff53f9SFlorian Fainelli 	return err;
74004ff53f9SFlorian Fainelli }
74104ff53f9SFlorian Fainelli 
74204ff53f9SFlorian Fainelli static void dsa_slave_netpoll_cleanup(struct net_device *dev)
74304ff53f9SFlorian Fainelli {
74404ff53f9SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
74504ff53f9SFlorian Fainelli 	struct netpoll *netpoll = p->netpoll;
74604ff53f9SFlorian Fainelli 
74704ff53f9SFlorian Fainelli 	if (!netpoll)
74804ff53f9SFlorian Fainelli 		return;
74904ff53f9SFlorian Fainelli 
75004ff53f9SFlorian Fainelli 	p->netpoll = NULL;
75104ff53f9SFlorian Fainelli 
75204ff53f9SFlorian Fainelli 	__netpoll_free_async(netpoll);
75304ff53f9SFlorian Fainelli }
75404ff53f9SFlorian Fainelli 
75504ff53f9SFlorian Fainelli static void dsa_slave_poll_controller(struct net_device *dev)
75604ff53f9SFlorian Fainelli {
75704ff53f9SFlorian Fainelli }
75804ff53f9SFlorian Fainelli #endif
75904ff53f9SFlorian Fainelli 
76091da11f8SLennert Buytenhek static const struct ethtool_ops dsa_slave_ethtool_ops = {
76191da11f8SLennert Buytenhek 	.get_settings		= dsa_slave_get_settings,
76291da11f8SLennert Buytenhek 	.set_settings		= dsa_slave_set_settings,
76391da11f8SLennert Buytenhek 	.get_drvinfo		= dsa_slave_get_drvinfo,
7643d762a0fSGuenter Roeck 	.get_regs_len		= dsa_slave_get_regs_len,
7653d762a0fSGuenter Roeck 	.get_regs		= dsa_slave_get_regs,
76691da11f8SLennert Buytenhek 	.nway_reset		= dsa_slave_nway_reset,
76791da11f8SLennert Buytenhek 	.get_link		= dsa_slave_get_link,
7686793abb4SGuenter Roeck 	.get_eeprom_len		= dsa_slave_get_eeprom_len,
7696793abb4SGuenter Roeck 	.get_eeprom		= dsa_slave_get_eeprom,
7706793abb4SGuenter Roeck 	.set_eeprom		= dsa_slave_set_eeprom,
77191da11f8SLennert Buytenhek 	.get_strings		= dsa_slave_get_strings,
77291da11f8SLennert Buytenhek 	.get_ethtool_stats	= dsa_slave_get_ethtool_stats,
77391da11f8SLennert Buytenhek 	.get_sset_count		= dsa_slave_get_sset_count,
77419e57c4eSFlorian Fainelli 	.set_wol		= dsa_slave_set_wol,
77519e57c4eSFlorian Fainelli 	.get_wol		= dsa_slave_get_wol,
7767905288fSFlorian Fainelli 	.set_eee		= dsa_slave_set_eee,
7777905288fSFlorian Fainelli 	.get_eee		= dsa_slave_get_eee,
77891da11f8SLennert Buytenhek };
77991da11f8SLennert Buytenhek 
7803e8a72d1SFlorian Fainelli static const struct net_device_ops dsa_slave_netdev_ops = {
781d442ad4aSStephen Hemminger 	.ndo_open	 	= dsa_slave_open,
782d442ad4aSStephen Hemminger 	.ndo_stop		= dsa_slave_close,
7833e8a72d1SFlorian Fainelli 	.ndo_start_xmit		= dsa_slave_xmit,
784d442ad4aSStephen Hemminger 	.ndo_change_rx_flags	= dsa_slave_change_rx_flags,
785d442ad4aSStephen Hemminger 	.ndo_set_rx_mode	= dsa_slave_set_rx_mode,
786d442ad4aSStephen Hemminger 	.ndo_set_mac_address	= dsa_slave_set_mac_address,
787*ba14d9ebSVivien Didelot 	.ndo_fdb_add		= switchdev_port_fdb_add,
788*ba14d9ebSVivien Didelot 	.ndo_fdb_del		= switchdev_port_fdb_del,
789*ba14d9ebSVivien Didelot 	.ndo_fdb_dump		= switchdev_port_fdb_dump,
790d442ad4aSStephen Hemminger 	.ndo_do_ioctl		= dsa_slave_ioctl,
791abd2be00SNicolas Dichtel 	.ndo_get_iflink		= dsa_slave_get_iflink,
79204ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
79304ff53f9SFlorian Fainelli 	.ndo_netpoll_setup	= dsa_slave_netpoll_setup,
79404ff53f9SFlorian Fainelli 	.ndo_netpoll_cleanup	= dsa_slave_netpoll_cleanup,
79504ff53f9SFlorian Fainelli 	.ndo_poll_controller	= dsa_slave_poll_controller,
79604ff53f9SFlorian Fainelli #endif
79798237d43SScott Feldman };
79898237d43SScott Feldman 
7999d47c0a2SJiri Pirko static const struct switchdev_ops dsa_slave_switchdev_ops = {
800f8e20a9fSScott Feldman 	.switchdev_port_attr_get	= dsa_slave_port_attr_get,
80135636062SScott Feldman 	.switchdev_port_attr_set	= dsa_slave_port_attr_set,
802*ba14d9ebSVivien Didelot 	.switchdev_port_obj_add		= dsa_slave_port_obj_add,
803*ba14d9ebSVivien Didelot 	.switchdev_port_obj_del		= dsa_slave_port_obj_del,
804*ba14d9ebSVivien Didelot 	.switchdev_port_obj_dump	= dsa_slave_port_obj_dump,
805d442ad4aSStephen Hemminger };
80691da11f8SLennert Buytenhek 
8070d8bcdd3SFlorian Fainelli static void dsa_slave_adjust_link(struct net_device *dev)
8080d8bcdd3SFlorian Fainelli {
8090d8bcdd3SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
810ec9436baSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
8110d8bcdd3SFlorian Fainelli 	unsigned int status_changed = 0;
8120d8bcdd3SFlorian Fainelli 
8130d8bcdd3SFlorian Fainelli 	if (p->old_link != p->phy->link) {
8140d8bcdd3SFlorian Fainelli 		status_changed = 1;
8150d8bcdd3SFlorian Fainelli 		p->old_link = p->phy->link;
8160d8bcdd3SFlorian Fainelli 	}
8170d8bcdd3SFlorian Fainelli 
8180d8bcdd3SFlorian Fainelli 	if (p->old_duplex != p->phy->duplex) {
8190d8bcdd3SFlorian Fainelli 		status_changed = 1;
8200d8bcdd3SFlorian Fainelli 		p->old_duplex = p->phy->duplex;
8210d8bcdd3SFlorian Fainelli 	}
8220d8bcdd3SFlorian Fainelli 
8230d8bcdd3SFlorian Fainelli 	if (p->old_pause != p->phy->pause) {
8240d8bcdd3SFlorian Fainelli 		status_changed = 1;
8250d8bcdd3SFlorian Fainelli 		p->old_pause = p->phy->pause;
8260d8bcdd3SFlorian Fainelli 	}
8270d8bcdd3SFlorian Fainelli 
828ec9436baSFlorian Fainelli 	if (ds->drv->adjust_link && status_changed)
829ec9436baSFlorian Fainelli 		ds->drv->adjust_link(ds, p->port, p->phy);
830ec9436baSFlorian Fainelli 
8310d8bcdd3SFlorian Fainelli 	if (status_changed)
8320d8bcdd3SFlorian Fainelli 		phy_print_status(p->phy);
8330d8bcdd3SFlorian Fainelli }
8340d8bcdd3SFlorian Fainelli 
835ce31b31cSFlorian Fainelli static int dsa_slave_fixed_link_update(struct net_device *dev,
836ce31b31cSFlorian Fainelli 				       struct fixed_phy_status *status)
837ce31b31cSFlorian Fainelli {
838ce31b31cSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
839ce31b31cSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
840ce31b31cSFlorian Fainelli 
841ce31b31cSFlorian Fainelli 	if (ds->drv->fixed_link_update)
842ce31b31cSFlorian Fainelli 		ds->drv->fixed_link_update(ds, p->port, status);
843ce31b31cSFlorian Fainelli 
844ce31b31cSFlorian Fainelli 	return 0;
845ce31b31cSFlorian Fainelli }
846ce31b31cSFlorian Fainelli 
84791da11f8SLennert Buytenhek /* slave device setup *******************************************************/
848c305c165SFlorian Fainelli static int dsa_slave_phy_connect(struct dsa_slave_priv *p,
849cd28a1a9SFlorian Fainelli 				 struct net_device *slave_dev,
850cd28a1a9SFlorian Fainelli 				 int addr)
851c305c165SFlorian Fainelli {
852c305c165SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
853c305c165SFlorian Fainelli 
854cd28a1a9SFlorian Fainelli 	p->phy = ds->slave_mii_bus->phy_map[addr];
855c305c165SFlorian Fainelli 	if (!p->phy)
856c305c165SFlorian Fainelli 		return -ENODEV;
857c305c165SFlorian Fainelli 
858c305c165SFlorian Fainelli 	/* Use already configured phy mode */
859c305c165SFlorian Fainelli 	p->phy_interface = p->phy->interface;
860c305c165SFlorian Fainelli 	phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
861c305c165SFlorian Fainelli 			   p->phy_interface);
862c305c165SFlorian Fainelli 
863c305c165SFlorian Fainelli 	return 0;
864c305c165SFlorian Fainelli }
865c305c165SFlorian Fainelli 
8669697f1cdSFlorian Fainelli static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
8670d8bcdd3SFlorian Fainelli 				struct net_device *slave_dev)
8680d8bcdd3SFlorian Fainelli {
8690d8bcdd3SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
8700d8bcdd3SFlorian Fainelli 	struct dsa_chip_data *cd = ds->pd;
8710d8bcdd3SFlorian Fainelli 	struct device_node *phy_dn, *port_dn;
872ce31b31cSFlorian Fainelli 	bool phy_is_fixed = false;
8736819563eSFlorian Fainelli 	u32 phy_flags = 0;
87419334920SGuenter Roeck 	int mode, ret;
8750d8bcdd3SFlorian Fainelli 
8760d8bcdd3SFlorian Fainelli 	port_dn = cd->port_dn[p->port];
87719334920SGuenter Roeck 	mode = of_get_phy_mode(port_dn);
87819334920SGuenter Roeck 	if (mode < 0)
87919334920SGuenter Roeck 		mode = PHY_INTERFACE_MODE_NA;
88019334920SGuenter Roeck 	p->phy_interface = mode;
8810d8bcdd3SFlorian Fainelli 
8820d8bcdd3SFlorian Fainelli 	phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
8830d8bcdd3SFlorian Fainelli 	if (of_phy_is_fixed_link(port_dn)) {
8840d8bcdd3SFlorian Fainelli 		/* In the case of a fixed PHY, the DT node associated
8850d8bcdd3SFlorian Fainelli 		 * to the fixed PHY is the Port DT node
8860d8bcdd3SFlorian Fainelli 		 */
8870d8bcdd3SFlorian Fainelli 		ret = of_phy_register_fixed_link(port_dn);
8880d8bcdd3SFlorian Fainelli 		if (ret) {
889a2ae6007SJoe Perches 			netdev_err(slave_dev, "failed to register fixed PHY\n");
8909697f1cdSFlorian Fainelli 			return ret;
8910d8bcdd3SFlorian Fainelli 		}
892ce31b31cSFlorian Fainelli 		phy_is_fixed = true;
8930d8bcdd3SFlorian Fainelli 		phy_dn = port_dn;
8940d8bcdd3SFlorian Fainelli 	}
8950d8bcdd3SFlorian Fainelli 
8966819563eSFlorian Fainelli 	if (ds->drv->get_phy_flags)
8976819563eSFlorian Fainelli 		phy_flags = ds->drv->get_phy_flags(ds, p->port);
8986819563eSFlorian Fainelli 
899cd28a1a9SFlorian Fainelli 	if (phy_dn) {
900cd28a1a9SFlorian Fainelli 		ret = of_mdio_parse_addr(&slave_dev->dev, phy_dn);
901cd28a1a9SFlorian Fainelli 		/* If this PHY address is part of phys_mii_mask, which means
902cd28a1a9SFlorian Fainelli 		 * that we need to divert reads and writes to/from it, then we
903cd28a1a9SFlorian Fainelli 		 * want to bind this device using the slave MII bus created by
904cd28a1a9SFlorian Fainelli 		 * DSA to make that happen.
905cd28a1a9SFlorian Fainelli 		 */
90696026d05SFlorian Fainelli 		if (!phy_is_fixed && ret >= 0 &&
90796026d05SFlorian Fainelli 		    (ds->phys_mii_mask & (1 << ret))) {
908cd28a1a9SFlorian Fainelli 			ret = dsa_slave_phy_connect(p, slave_dev, ret);
909cd28a1a9SFlorian Fainelli 			if (ret)
910cd28a1a9SFlorian Fainelli 				return ret;
911cd28a1a9SFlorian Fainelli 		} else {
9120d8bcdd3SFlorian Fainelli 			p->phy = of_phy_connect(slave_dev, phy_dn,
913cd28a1a9SFlorian Fainelli 						dsa_slave_adjust_link,
914cd28a1a9SFlorian Fainelli 						phy_flags,
9150d8bcdd3SFlorian Fainelli 						p->phy_interface);
916cd28a1a9SFlorian Fainelli 		}
917cd28a1a9SFlorian Fainelli 	}
9180d8bcdd3SFlorian Fainelli 
919ce31b31cSFlorian Fainelli 	if (p->phy && phy_is_fixed)
920ce31b31cSFlorian Fainelli 		fixed_phy_set_link_update(p->phy, dsa_slave_fixed_link_update);
921ce31b31cSFlorian Fainelli 
9220d8bcdd3SFlorian Fainelli 	/* We could not connect to a designated PHY, so use the switch internal
9230d8bcdd3SFlorian Fainelli 	 * MDIO bus instead
9240d8bcdd3SFlorian Fainelli 	 */
925b31f65fbSAndrew Lunn 	if (!p->phy) {
926cd28a1a9SFlorian Fainelli 		ret = dsa_slave_phy_connect(p, slave_dev, p->port);
927c305c165SFlorian Fainelli 		if (ret)
928c305c165SFlorian Fainelli 			return ret;
929b31f65fbSAndrew Lunn 	} else {
930a2ae6007SJoe Perches 		netdev_info(slave_dev, "attached PHY at address %d [%s]\n",
9310d8bcdd3SFlorian Fainelli 			    p->phy->addr, p->phy->drv->name);
9320d8bcdd3SFlorian Fainelli 	}
9339697f1cdSFlorian Fainelli 
9349697f1cdSFlorian Fainelli 	return 0;
935b31f65fbSAndrew Lunn }
9360d8bcdd3SFlorian Fainelli 
937448b4482SAndrew Lunn static struct lock_class_key dsa_slave_netdev_xmit_lock_key;
938448b4482SAndrew Lunn static void dsa_slave_set_lockdep_class_one(struct net_device *dev,
939448b4482SAndrew Lunn 					    struct netdev_queue *txq,
940448b4482SAndrew Lunn 					    void *_unused)
941448b4482SAndrew Lunn {
942448b4482SAndrew Lunn 	lockdep_set_class(&txq->_xmit_lock,
943448b4482SAndrew Lunn 			  &dsa_slave_netdev_xmit_lock_key);
944448b4482SAndrew Lunn }
945448b4482SAndrew Lunn 
94624462549SFlorian Fainelli int dsa_slave_suspend(struct net_device *slave_dev)
94724462549SFlorian Fainelli {
94824462549SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(slave_dev);
94924462549SFlorian Fainelli 
95024462549SFlorian Fainelli 	if (p->phy) {
95124462549SFlorian Fainelli 		phy_stop(p->phy);
95224462549SFlorian Fainelli 		p->old_pause = -1;
95324462549SFlorian Fainelli 		p->old_link = -1;
95424462549SFlorian Fainelli 		p->old_duplex = -1;
95524462549SFlorian Fainelli 		phy_suspend(p->phy);
95624462549SFlorian Fainelli 	}
95724462549SFlorian Fainelli 
95824462549SFlorian Fainelli 	return 0;
95924462549SFlorian Fainelli }
96024462549SFlorian Fainelli 
96124462549SFlorian Fainelli int dsa_slave_resume(struct net_device *slave_dev)
96224462549SFlorian Fainelli {
96324462549SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(slave_dev);
96424462549SFlorian Fainelli 
96524462549SFlorian Fainelli 	netif_device_attach(slave_dev);
96624462549SFlorian Fainelli 
96724462549SFlorian Fainelli 	if (p->phy) {
96824462549SFlorian Fainelli 		phy_resume(p->phy);
96924462549SFlorian Fainelli 		phy_start(p->phy);
97024462549SFlorian Fainelli 	}
97124462549SFlorian Fainelli 
97224462549SFlorian Fainelli 	return 0;
97324462549SFlorian Fainelli }
97424462549SFlorian Fainelli 
975d87d6f44SGuenter Roeck int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
97691da11f8SLennert Buytenhek 		     int port, char *name)
97791da11f8SLennert Buytenhek {
978e84665c9SLennert Buytenhek 	struct net_device *master = ds->dst->master_netdev;
97991da11f8SLennert Buytenhek 	struct net_device *slave_dev;
98091da11f8SLennert Buytenhek 	struct dsa_slave_priv *p;
98191da11f8SLennert Buytenhek 	int ret;
98291da11f8SLennert Buytenhek 
983c835a677STom Gundersen 	slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name,
984c835a677STom Gundersen 				 NET_NAME_UNKNOWN, ether_setup);
98591da11f8SLennert Buytenhek 	if (slave_dev == NULL)
986d87d6f44SGuenter Roeck 		return -ENOMEM;
98791da11f8SLennert Buytenhek 
98891da11f8SLennert Buytenhek 	slave_dev->features = master->vlan_features;
9897ad24ea4SWilfried Klaebe 	slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
9902fcc8005SBjørn Mork 	eth_hw_addr_inherit(slave_dev, master);
99191da11f8SLennert Buytenhek 	slave_dev->tx_queue_len = 0;
9923e8a72d1SFlorian Fainelli 	slave_dev->netdev_ops = &dsa_slave_netdev_ops;
9939d47c0a2SJiri Pirko 	slave_dev->switchdev_ops = &dsa_slave_switchdev_ops;
994d442ad4aSStephen Hemminger 
995448b4482SAndrew Lunn 	netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one,
996448b4482SAndrew Lunn 				 NULL);
997448b4482SAndrew Lunn 
99891da11f8SLennert Buytenhek 	SET_NETDEV_DEV(slave_dev, parent);
999bd47497aSFlorian Fainelli 	slave_dev->dev.of_node = ds->pd->port_dn[port];
100091da11f8SLennert Buytenhek 	slave_dev->vlan_features = master->vlan_features;
100191da11f8SLennert Buytenhek 
100291da11f8SLennert Buytenhek 	p = netdev_priv(slave_dev);
100391da11f8SLennert Buytenhek 	p->dev = slave_dev;
100491da11f8SLennert Buytenhek 	p->parent = ds;
100591da11f8SLennert Buytenhek 	p->port = port;
10060d8bcdd3SFlorian Fainelli 
10075075314eSAlexander Duyck 	switch (ds->dst->tag_protocol) {
10085075314eSAlexander Duyck #ifdef CONFIG_NET_DSA_TAG_DSA
10095075314eSAlexander Duyck 	case DSA_TAG_PROTO_DSA:
10105075314eSAlexander Duyck 		p->xmit = dsa_netdev_ops.xmit;
10115075314eSAlexander Duyck 		break;
10125075314eSAlexander Duyck #endif
10135075314eSAlexander Duyck #ifdef CONFIG_NET_DSA_TAG_EDSA
10145075314eSAlexander Duyck 	case DSA_TAG_PROTO_EDSA:
10155075314eSAlexander Duyck 		p->xmit = edsa_netdev_ops.xmit;
10165075314eSAlexander Duyck 		break;
10175075314eSAlexander Duyck #endif
10185075314eSAlexander Duyck #ifdef CONFIG_NET_DSA_TAG_TRAILER
10195075314eSAlexander Duyck 	case DSA_TAG_PROTO_TRAILER:
10205075314eSAlexander Duyck 		p->xmit = trailer_netdev_ops.xmit;
10215075314eSAlexander Duyck 		break;
10225075314eSAlexander Duyck #endif
10235075314eSAlexander Duyck #ifdef CONFIG_NET_DSA_TAG_BRCM
10245075314eSAlexander Duyck 	case DSA_TAG_PROTO_BRCM:
10255075314eSAlexander Duyck 		p->xmit = brcm_netdev_ops.xmit;
10265075314eSAlexander Duyck 		break;
10275075314eSAlexander Duyck #endif
10285075314eSAlexander Duyck 	default:
10295075314eSAlexander Duyck 		p->xmit	= dsa_slave_notag_xmit;
10305075314eSAlexander Duyck 		break;
10315075314eSAlexander Duyck 	}
10325075314eSAlexander Duyck 
10330d8bcdd3SFlorian Fainelli 	p->old_pause = -1;
10340d8bcdd3SFlorian Fainelli 	p->old_link = -1;
10350d8bcdd3SFlorian Fainelli 	p->old_duplex = -1;
10360d8bcdd3SFlorian Fainelli 
10379697f1cdSFlorian Fainelli 	ret = dsa_slave_phy_setup(p, slave_dev);
10389697f1cdSFlorian Fainelli 	if (ret) {
10399697f1cdSFlorian Fainelli 		free_netdev(slave_dev);
1040d87d6f44SGuenter Roeck 		return ret;
10419697f1cdSFlorian Fainelli 	}
104291da11f8SLennert Buytenhek 
1043d87d6f44SGuenter Roeck 	ds->ports[port] = slave_dev;
104491da11f8SLennert Buytenhek 	ret = register_netdev(slave_dev);
104591da11f8SLennert Buytenhek 	if (ret) {
1046a2ae6007SJoe Perches 		netdev_err(master, "error %d registering interface %s\n",
1047a2ae6007SJoe Perches 			   ret, slave_dev->name);
10489697f1cdSFlorian Fainelli 		phy_disconnect(p->phy);
1049d87d6f44SGuenter Roeck 		ds->ports[port] = NULL;
105091da11f8SLennert Buytenhek 		free_netdev(slave_dev);
1051d87d6f44SGuenter Roeck 		return ret;
105291da11f8SLennert Buytenhek 	}
105391da11f8SLennert Buytenhek 
105491da11f8SLennert Buytenhek 	netif_carrier_off(slave_dev);
105591da11f8SLennert Buytenhek 
1056d87d6f44SGuenter Roeck 	return 0;
105791da11f8SLennert Buytenhek }
1058b73adef6SFlorian Fainelli 
1059b73adef6SFlorian Fainelli static bool dsa_slave_dev_check(struct net_device *dev)
1060b73adef6SFlorian Fainelli {
1061b73adef6SFlorian Fainelli 	return dev->netdev_ops == &dsa_slave_netdev_ops;
1062b73adef6SFlorian Fainelli }
1063b73adef6SFlorian Fainelli 
1064b73adef6SFlorian Fainelli static int dsa_slave_master_changed(struct net_device *dev)
1065b73adef6SFlorian Fainelli {
1066b73adef6SFlorian Fainelli 	struct net_device *master = netdev_master_upper_dev_get(dev);
1067b06b107aSGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
1068b73adef6SFlorian Fainelli 	int err = 0;
1069b73adef6SFlorian Fainelli 
1070b73adef6SFlorian Fainelli 	if (master && master->rtnl_link_ops &&
1071b73adef6SFlorian Fainelli 	    !strcmp(master->rtnl_link_ops->kind, "bridge"))
1072b73adef6SFlorian Fainelli 		err = dsa_slave_bridge_port_join(dev, master);
1073b06b107aSGuenter Roeck 	else if (dsa_port_is_bridged(p))
1074b73adef6SFlorian Fainelli 		err = dsa_slave_bridge_port_leave(dev);
1075b73adef6SFlorian Fainelli 
1076b73adef6SFlorian Fainelli 	return err;
1077b73adef6SFlorian Fainelli }
1078b73adef6SFlorian Fainelli 
1079b73adef6SFlorian Fainelli int dsa_slave_netdevice_event(struct notifier_block *unused,
1080b73adef6SFlorian Fainelli 			      unsigned long event, void *ptr)
1081b73adef6SFlorian Fainelli {
1082b73adef6SFlorian Fainelli 	struct net_device *dev;
1083b73adef6SFlorian Fainelli 	int err = 0;
1084b73adef6SFlorian Fainelli 
1085b73adef6SFlorian Fainelli 	switch (event) {
1086b73adef6SFlorian Fainelli 	case NETDEV_CHANGEUPPER:
1087b73adef6SFlorian Fainelli 		dev = netdev_notifier_info_to_dev(ptr);
1088b73adef6SFlorian Fainelli 		if (!dsa_slave_dev_check(dev))
1089b73adef6SFlorian Fainelli 			goto out;
1090b73adef6SFlorian Fainelli 
1091b73adef6SFlorian Fainelli 		err = dsa_slave_master_changed(dev);
1092b73adef6SFlorian Fainelli 		if (err)
1093b73adef6SFlorian Fainelli 			netdev_warn(dev, "failed to reflect master change\n");
1094b73adef6SFlorian Fainelli 
1095b73adef6SFlorian Fainelli 		break;
1096b73adef6SFlorian Fainelli 	}
1097b73adef6SFlorian Fainelli 
1098b73adef6SFlorian Fainelli out:
1099b73adef6SFlorian Fainelli 	return NOTIFY_DONE;
1100b73adef6SFlorian Fainelli }
1101