xref: /openbmc/linux/net/dsa/slave.c (revision 9d490b4ee4d7d495a4f4908ea998d2a7355e0807)
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>
187f854420SAndrew Lunn #include <linux/mdio.h>
19b73adef6SFlorian Fainelli #include <net/rtnetlink.h>
2098237d43SScott Feldman #include <net/switchdev.h>
21b73adef6SFlorian Fainelli #include <linux/if_bridge.h>
2204ff53f9SFlorian Fainelli #include <linux/netpoll.h>
2391da11f8SLennert Buytenhek #include "dsa_priv.h"
2491da11f8SLennert Buytenhek 
2591da11f8SLennert Buytenhek /* slave mii_bus handling ***************************************************/
2691da11f8SLennert Buytenhek static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
2791da11f8SLennert Buytenhek {
2891da11f8SLennert Buytenhek 	struct dsa_switch *ds = bus->priv;
2991da11f8SLennert Buytenhek 
300d8bcdd3SFlorian Fainelli 	if (ds->phys_mii_mask & (1 << addr))
31*9d490b4eSVivien Didelot 		return ds->ops->phy_read(ds, addr, reg);
3291da11f8SLennert Buytenhek 
3391da11f8SLennert Buytenhek 	return 0xffff;
3491da11f8SLennert Buytenhek }
3591da11f8SLennert Buytenhek 
3691da11f8SLennert Buytenhek static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
3791da11f8SLennert Buytenhek {
3891da11f8SLennert Buytenhek 	struct dsa_switch *ds = bus->priv;
3991da11f8SLennert Buytenhek 
400d8bcdd3SFlorian Fainelli 	if (ds->phys_mii_mask & (1 << addr))
41*9d490b4eSVivien Didelot 		return ds->ops->phy_write(ds, addr, reg, val);
4291da11f8SLennert Buytenhek 
4391da11f8SLennert Buytenhek 	return 0;
4491da11f8SLennert Buytenhek }
4591da11f8SLennert Buytenhek 
4691da11f8SLennert Buytenhek void dsa_slave_mii_bus_init(struct dsa_switch *ds)
4791da11f8SLennert Buytenhek {
4891da11f8SLennert Buytenhek 	ds->slave_mii_bus->priv = (void *)ds;
4991da11f8SLennert Buytenhek 	ds->slave_mii_bus->name = "dsa slave smi";
5091da11f8SLennert Buytenhek 	ds->slave_mii_bus->read = dsa_slave_phy_read;
5191da11f8SLennert Buytenhek 	ds->slave_mii_bus->write = dsa_slave_phy_write;
520b7b498dSFlorian Fainelli 	snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d",
530b7b498dSFlorian Fainelli 		 ds->dst->tree, ds->index);
54c33063d6SAndrew Lunn 	ds->slave_mii_bus->parent = ds->dev;
5524df8986SVivien Didelot 	ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
5691da11f8SLennert Buytenhek }
5791da11f8SLennert Buytenhek 
5891da11f8SLennert Buytenhek 
5991da11f8SLennert Buytenhek /* slave device handling ****************************************************/
60abd2be00SNicolas Dichtel static int dsa_slave_get_iflink(const struct net_device *dev)
61c0840801SLennert Buytenhek {
62c0840801SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
63c0840801SLennert Buytenhek 
64abd2be00SNicolas Dichtel 	return p->parent->dst->master_netdev->ifindex;
65c0840801SLennert Buytenhek }
66c0840801SLennert Buytenhek 
67b73adef6SFlorian Fainelli static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p)
68b73adef6SFlorian Fainelli {
69b73adef6SFlorian Fainelli 	return !!p->bridge_dev;
70b73adef6SFlorian Fainelli }
71b73adef6SFlorian Fainelli 
7291da11f8SLennert Buytenhek static int dsa_slave_open(struct net_device *dev)
7391da11f8SLennert Buytenhek {
74df02c6ffSLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
75e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
76b2f2af21SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
77b73adef6SFlorian Fainelli 	u8 stp_state = dsa_port_is_bridged(p) ?
78b73adef6SFlorian Fainelli 			BR_STATE_BLOCKING : BR_STATE_FORWARDING;
79df02c6ffSLennert Buytenhek 	int err;
80df02c6ffSLennert Buytenhek 
81df02c6ffSLennert Buytenhek 	if (!(master->flags & IFF_UP))
82df02c6ffSLennert Buytenhek 		return -ENETDOWN;
83df02c6ffSLennert Buytenhek 
848feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) {
85a748ee24SJiri Pirko 		err = dev_uc_add(master, dev->dev_addr);
86df02c6ffSLennert Buytenhek 		if (err < 0)
87df02c6ffSLennert Buytenhek 			goto out;
88df02c6ffSLennert Buytenhek 	}
89df02c6ffSLennert Buytenhek 
90df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_ALLMULTI) {
91df02c6ffSLennert Buytenhek 		err = dev_set_allmulti(master, 1);
92df02c6ffSLennert Buytenhek 		if (err < 0)
93df02c6ffSLennert Buytenhek 			goto del_unicast;
94df02c6ffSLennert Buytenhek 	}
95df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_PROMISC) {
96df02c6ffSLennert Buytenhek 		err = dev_set_promiscuity(master, 1);
97df02c6ffSLennert Buytenhek 		if (err < 0)
98df02c6ffSLennert Buytenhek 			goto clear_allmulti;
99df02c6ffSLennert Buytenhek 	}
100df02c6ffSLennert Buytenhek 
101*9d490b4eSVivien Didelot 	if (ds->ops->port_enable) {
102*9d490b4eSVivien Didelot 		err = ds->ops->port_enable(ds, p->port, p->phy);
103b2f2af21SFlorian Fainelli 		if (err)
104b2f2af21SFlorian Fainelli 			goto clear_promisc;
105b2f2af21SFlorian Fainelli 	}
106b2f2af21SFlorian Fainelli 
107*9d490b4eSVivien Didelot 	if (ds->ops->port_stp_state_set)
108*9d490b4eSVivien Didelot 		ds->ops->port_stp_state_set(ds, p->port, stp_state);
109b73adef6SFlorian Fainelli 
110f7f1de51SFlorian Fainelli 	if (p->phy)
111f7f1de51SFlorian Fainelli 		phy_start(p->phy);
112f7f1de51SFlorian Fainelli 
11391da11f8SLennert Buytenhek 	return 0;
114df02c6ffSLennert Buytenhek 
115b2f2af21SFlorian Fainelli clear_promisc:
116b2f2af21SFlorian Fainelli 	if (dev->flags & IFF_PROMISC)
1174fdeddfeSGilad Ben-Yossef 		dev_set_promiscuity(master, -1);
118df02c6ffSLennert Buytenhek clear_allmulti:
119df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_ALLMULTI)
120df02c6ffSLennert Buytenhek 		dev_set_allmulti(master, -1);
121df02c6ffSLennert Buytenhek del_unicast:
1228feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
123a748ee24SJiri Pirko 		dev_uc_del(master, dev->dev_addr);
124df02c6ffSLennert Buytenhek out:
125df02c6ffSLennert Buytenhek 	return err;
12691da11f8SLennert Buytenhek }
12791da11f8SLennert Buytenhek 
12891da11f8SLennert Buytenhek static int dsa_slave_close(struct net_device *dev)
12991da11f8SLennert Buytenhek {
130df02c6ffSLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
131e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
132b2f2af21SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
133df02c6ffSLennert Buytenhek 
134f7f1de51SFlorian Fainelli 	if (p->phy)
135f7f1de51SFlorian Fainelli 		phy_stop(p->phy);
136f7f1de51SFlorian Fainelli 
137df02c6ffSLennert Buytenhek 	dev_mc_unsync(master, dev);
138a748ee24SJiri Pirko 	dev_uc_unsync(master, dev);
139df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_ALLMULTI)
140df02c6ffSLennert Buytenhek 		dev_set_allmulti(master, -1);
141df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_PROMISC)
142df02c6ffSLennert Buytenhek 		dev_set_promiscuity(master, -1);
143df02c6ffSLennert Buytenhek 
1448feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
145a748ee24SJiri Pirko 		dev_uc_del(master, dev->dev_addr);
146df02c6ffSLennert Buytenhek 
147*9d490b4eSVivien Didelot 	if (ds->ops->port_disable)
148*9d490b4eSVivien Didelot 		ds->ops->port_disable(ds, p->port, p->phy);
149b2f2af21SFlorian Fainelli 
150*9d490b4eSVivien Didelot 	if (ds->ops->port_stp_state_set)
151*9d490b4eSVivien Didelot 		ds->ops->port_stp_state_set(ds, p->port, BR_STATE_DISABLED);
152b73adef6SFlorian Fainelli 
15391da11f8SLennert Buytenhek 	return 0;
15491da11f8SLennert Buytenhek }
15591da11f8SLennert Buytenhek 
15691da11f8SLennert Buytenhek static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
15791da11f8SLennert Buytenhek {
15891da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
159e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
16091da11f8SLennert Buytenhek 
16191da11f8SLennert Buytenhek 	if (change & IFF_ALLMULTI)
16291da11f8SLennert Buytenhek 		dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
16391da11f8SLennert Buytenhek 	if (change & IFF_PROMISC)
16491da11f8SLennert Buytenhek 		dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1);
16591da11f8SLennert Buytenhek }
16691da11f8SLennert Buytenhek 
16791da11f8SLennert Buytenhek static void dsa_slave_set_rx_mode(struct net_device *dev)
16891da11f8SLennert Buytenhek {
16991da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
170e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
17191da11f8SLennert Buytenhek 
17291da11f8SLennert Buytenhek 	dev_mc_sync(master, dev);
173a748ee24SJiri Pirko 	dev_uc_sync(master, dev);
17491da11f8SLennert Buytenhek }
17591da11f8SLennert Buytenhek 
176df02c6ffSLennert Buytenhek static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
17791da11f8SLennert Buytenhek {
178df02c6ffSLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
179e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
180df02c6ffSLennert Buytenhek 	struct sockaddr *addr = a;
181df02c6ffSLennert Buytenhek 	int err;
182df02c6ffSLennert Buytenhek 
183df02c6ffSLennert Buytenhek 	if (!is_valid_ether_addr(addr->sa_data))
184df02c6ffSLennert Buytenhek 		return -EADDRNOTAVAIL;
185df02c6ffSLennert Buytenhek 
186df02c6ffSLennert Buytenhek 	if (!(dev->flags & IFF_UP))
187df02c6ffSLennert Buytenhek 		goto out;
188df02c6ffSLennert Buytenhek 
1898feedbb4SJoe Perches 	if (!ether_addr_equal(addr->sa_data, master->dev_addr)) {
190a748ee24SJiri Pirko 		err = dev_uc_add(master, addr->sa_data);
191df02c6ffSLennert Buytenhek 		if (err < 0)
192df02c6ffSLennert Buytenhek 			return err;
193df02c6ffSLennert Buytenhek 	}
194df02c6ffSLennert Buytenhek 
1958feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
196a748ee24SJiri Pirko 		dev_uc_del(master, dev->dev_addr);
197df02c6ffSLennert Buytenhek 
198df02c6ffSLennert Buytenhek out:
199d08f161aSJoe Perches 	ether_addr_copy(dev->dev_addr, addr->sa_data);
20091da11f8SLennert Buytenhek 
20191da11f8SLennert Buytenhek 	return 0;
20291da11f8SLennert Buytenhek }
20391da11f8SLennert Buytenhek 
20411149536SVivien Didelot static int dsa_slave_port_vlan_add(struct net_device *dev,
2058f24f309SJiri Pirko 				   const struct switchdev_obj_port_vlan *vlan,
206f8db8348SJiri Pirko 				   struct switchdev_trans *trans)
20711149536SVivien Didelot {
20811149536SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
20911149536SVivien Didelot 	struct dsa_switch *ds = p->parent;
21011149536SVivien Didelot 
21179a62eb2SJiri Pirko 	if (switchdev_trans_ph_prepare(trans)) {
212*9d490b4eSVivien Didelot 		if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
21311149536SVivien Didelot 			return -EOPNOTSUPP;
21411149536SVivien Didelot 
215*9d490b4eSVivien Didelot 		return ds->ops->port_vlan_prepare(ds, p->port, vlan, trans);
21611149536SVivien Didelot 	}
21711149536SVivien Didelot 
218*9d490b4eSVivien Didelot 	ds->ops->port_vlan_add(ds, p->port, vlan, trans);
2194d5770b3SVivien Didelot 
22011149536SVivien Didelot 	return 0;
22111149536SVivien Didelot }
22211149536SVivien Didelot 
22311149536SVivien Didelot static int dsa_slave_port_vlan_del(struct net_device *dev,
2248f24f309SJiri Pirko 				   const struct switchdev_obj_port_vlan *vlan)
22511149536SVivien Didelot {
22611149536SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
22711149536SVivien Didelot 	struct dsa_switch *ds = p->parent;
22811149536SVivien Didelot 
229*9d490b4eSVivien Didelot 	if (!ds->ops->port_vlan_del)
23011149536SVivien Didelot 		return -EOPNOTSUPP;
23111149536SVivien Didelot 
232*9d490b4eSVivien Didelot 	return ds->ops->port_vlan_del(ds, p->port, vlan);
23311149536SVivien Didelot }
23411149536SVivien Didelot 
23511149536SVivien Didelot static int dsa_slave_port_vlan_dump(struct net_device *dev,
2368f24f309SJiri Pirko 				    struct switchdev_obj_port_vlan *vlan,
237648b4a99SJiri Pirko 				    switchdev_obj_dump_cb_t *cb)
23811149536SVivien Didelot {
23911149536SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
24011149536SVivien Didelot 	struct dsa_switch *ds = p->parent;
24111149536SVivien Didelot 
242*9d490b4eSVivien Didelot 	if (ds->ops->port_vlan_dump)
243*9d490b4eSVivien Didelot 		return ds->ops->port_vlan_dump(ds, p->port, vlan, cb);
24465aebfc0SVivien Didelot 
24511149536SVivien Didelot 	return -EOPNOTSUPP;
24611149536SVivien Didelot }
24711149536SVivien Didelot 
248ba14d9ebSVivien Didelot static int dsa_slave_port_fdb_add(struct net_device *dev,
24952ba57cfSJiri Pirko 				  const struct switchdev_obj_port_fdb *fdb,
250f8db8348SJiri Pirko 				  struct switchdev_trans *trans)
251cdf09697SDavid S. Miller {
252cdf09697SDavid S. Miller 	struct dsa_slave_priv *p = netdev_priv(dev);
253cdf09697SDavid S. Miller 	struct dsa_switch *ds = p->parent;
254146a3206SVivien Didelot 
2558497aa61SVivien Didelot 	if (switchdev_trans_ph_prepare(trans)) {
256*9d490b4eSVivien Didelot 		if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add)
257146a3206SVivien Didelot 			return -EOPNOTSUPP;
258cdf09697SDavid S. Miller 
259*9d490b4eSVivien Didelot 		return ds->ops->port_fdb_prepare(ds, p->port, fdb, trans);
2608497aa61SVivien Didelot 	}
261cdf09697SDavid S. Miller 
262*9d490b4eSVivien Didelot 	ds->ops->port_fdb_add(ds, p->port, fdb, trans);
2638497aa61SVivien Didelot 
2648497aa61SVivien Didelot 	return 0;
265cdf09697SDavid S. Miller }
266cdf09697SDavid S. Miller 
267ba14d9ebSVivien Didelot static int dsa_slave_port_fdb_del(struct net_device *dev,
26852ba57cfSJiri Pirko 				  const struct switchdev_obj_port_fdb *fdb)
269cdf09697SDavid S. Miller {
270cdf09697SDavid S. Miller 	struct dsa_slave_priv *p = netdev_priv(dev);
271cdf09697SDavid S. Miller 	struct dsa_switch *ds = p->parent;
272cdf09697SDavid S. Miller 	int ret = -EOPNOTSUPP;
273cdf09697SDavid S. Miller 
274*9d490b4eSVivien Didelot 	if (ds->ops->port_fdb_del)
275*9d490b4eSVivien Didelot 		ret = ds->ops->port_fdb_del(ds, p->port, fdb);
276cdf09697SDavid S. Miller 
277cdf09697SDavid S. Miller 	return ret;
278cdf09697SDavid S. Miller }
279cdf09697SDavid S. Miller 
280ba14d9ebSVivien Didelot static int dsa_slave_port_fdb_dump(struct net_device *dev,
28152ba57cfSJiri Pirko 				   struct switchdev_obj_port_fdb *fdb,
282648b4a99SJiri Pirko 				   switchdev_obj_dump_cb_t *cb)
283cdf09697SDavid S. Miller {
284cdf09697SDavid S. Miller 	struct dsa_slave_priv *p = netdev_priv(dev);
285cdf09697SDavid S. Miller 	struct dsa_switch *ds = p->parent;
286cdf09697SDavid S. Miller 
287*9d490b4eSVivien Didelot 	if (ds->ops->port_fdb_dump)
288*9d490b4eSVivien Didelot 		return ds->ops->port_fdb_dump(ds, p->port, fdb, cb);
289ea70ba98SVivien Didelot 
290cdf09697SDavid S. Miller 	return -EOPNOTSUPP;
291cdf09697SDavid S. Miller }
292cdf09697SDavid S. Miller 
29391da11f8SLennert Buytenhek static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
29491da11f8SLennert Buytenhek {
29591da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
29691da11f8SLennert Buytenhek 
29791da11f8SLennert Buytenhek 	if (p->phy != NULL)
29828b04113SRichard Cochran 		return phy_mii_ioctl(p->phy, ifr, cmd);
29991da11f8SLennert Buytenhek 
30091da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
30191da11f8SLennert Buytenhek }
30291da11f8SLennert Buytenhek 
30343c44a9fSVivien Didelot static int dsa_slave_stp_state_set(struct net_device *dev,
30443c44a9fSVivien Didelot 				   const struct switchdev_attr *attr,
30543c44a9fSVivien Didelot 				   struct switchdev_trans *trans)
306b73adef6SFlorian Fainelli {
307b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
308b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
309b73adef6SFlorian Fainelli 
31043c44a9fSVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
311*9d490b4eSVivien Didelot 		return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP;
312b73adef6SFlorian Fainelli 
313*9d490b4eSVivien Didelot 	ds->ops->port_stp_state_set(ds, p->port, attr->u.stp_state);
31443c44a9fSVivien Didelot 
31543c44a9fSVivien Didelot 	return 0;
316b73adef6SFlorian Fainelli }
317b73adef6SFlorian Fainelli 
318fb2dabadSVivien Didelot static int dsa_slave_vlan_filtering(struct net_device *dev,
319fb2dabadSVivien Didelot 				    const struct switchdev_attr *attr,
320fb2dabadSVivien Didelot 				    struct switchdev_trans *trans)
321fb2dabadSVivien Didelot {
322fb2dabadSVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
323fb2dabadSVivien Didelot 	struct dsa_switch *ds = p->parent;
324fb2dabadSVivien Didelot 
325fb2dabadSVivien Didelot 	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
326fb2dabadSVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
327fb2dabadSVivien Didelot 		return 0;
328fb2dabadSVivien Didelot 
329*9d490b4eSVivien Didelot 	if (ds->ops->port_vlan_filtering)
330*9d490b4eSVivien Didelot 		return ds->ops->port_vlan_filtering(ds, p->port,
331fb2dabadSVivien Didelot 						    attr->u.vlan_filtering);
332fb2dabadSVivien Didelot 
333fb2dabadSVivien Didelot 	return 0;
334fb2dabadSVivien Didelot }
335fb2dabadSVivien Didelot 
33634a79f63SVivien Didelot static int dsa_fastest_ageing_time(struct dsa_switch *ds,
33734a79f63SVivien Didelot 				   unsigned int ageing_time)
33834a79f63SVivien Didelot {
33934a79f63SVivien Didelot 	int i;
34034a79f63SVivien Didelot 
34134a79f63SVivien Didelot 	for (i = 0; i < DSA_MAX_PORTS; ++i) {
34234a79f63SVivien Didelot 		struct dsa_port *dp = &ds->ports[i];
34334a79f63SVivien Didelot 
34434a79f63SVivien Didelot 		if (dp && dp->ageing_time && dp->ageing_time < ageing_time)
34534a79f63SVivien Didelot 			ageing_time = dp->ageing_time;
34634a79f63SVivien Didelot 	}
34734a79f63SVivien Didelot 
34834a79f63SVivien Didelot 	return ageing_time;
34934a79f63SVivien Didelot }
35034a79f63SVivien Didelot 
35134a79f63SVivien Didelot static int dsa_slave_ageing_time(struct net_device *dev,
35234a79f63SVivien Didelot 				 const struct switchdev_attr *attr,
35334a79f63SVivien Didelot 				 struct switchdev_trans *trans)
35434a79f63SVivien Didelot {
35534a79f63SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
35634a79f63SVivien Didelot 	struct dsa_switch *ds = p->parent;
35734a79f63SVivien Didelot 	unsigned long ageing_jiffies = clock_t_to_jiffies(attr->u.ageing_time);
35834a79f63SVivien Didelot 	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
35934a79f63SVivien Didelot 
36034a79f63SVivien Didelot 	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
36134a79f63SVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
36234a79f63SVivien Didelot 		return 0;
36334a79f63SVivien Didelot 
36434a79f63SVivien Didelot 	/* Keep the fastest ageing time in case of multiple bridges */
36534a79f63SVivien Didelot 	ds->ports[p->port].ageing_time = ageing_time;
36634a79f63SVivien Didelot 	ageing_time = dsa_fastest_ageing_time(ds, ageing_time);
36734a79f63SVivien Didelot 
368*9d490b4eSVivien Didelot 	if (ds->ops->set_ageing_time)
369*9d490b4eSVivien Didelot 		return ds->ops->set_ageing_time(ds, ageing_time);
37034a79f63SVivien Didelot 
37134a79f63SVivien Didelot 	return 0;
37234a79f63SVivien Didelot }
37334a79f63SVivien Didelot 
37435636062SScott Feldman static int dsa_slave_port_attr_set(struct net_device *dev,
375f7fadf30SJiri Pirko 				   const struct switchdev_attr *attr,
3767ea6eb3fSJiri Pirko 				   struct switchdev_trans *trans)
37735636062SScott Feldman {
378b8d866acSVivien Didelot 	int ret;
37935636062SScott Feldman 
38035636062SScott Feldman 	switch (attr->id) {
3811f868398SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
38243c44a9fSVivien Didelot 		ret = dsa_slave_stp_state_set(dev, attr, trans);
38335636062SScott Feldman 		break;
384fb2dabadSVivien Didelot 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
385fb2dabadSVivien Didelot 		ret = dsa_slave_vlan_filtering(dev, attr, trans);
386fb2dabadSVivien Didelot 		break;
38734a79f63SVivien Didelot 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
38834a79f63SVivien Didelot 		ret = dsa_slave_ageing_time(dev, attr, trans);
38934a79f63SVivien Didelot 		break;
39035636062SScott Feldman 	default:
39135636062SScott Feldman 		ret = -EOPNOTSUPP;
39235636062SScott Feldman 		break;
39335636062SScott Feldman 	}
39435636062SScott Feldman 
39535636062SScott Feldman 	return ret;
39635636062SScott Feldman }
39735636062SScott Feldman 
398ba14d9ebSVivien Didelot static int dsa_slave_port_obj_add(struct net_device *dev,
399648b4a99SJiri Pirko 				  const struct switchdev_obj *obj,
4007ea6eb3fSJiri Pirko 				  struct switchdev_trans *trans)
401ba14d9ebSVivien Didelot {
402ba14d9ebSVivien Didelot 	int err;
403ba14d9ebSVivien Didelot 
404ba14d9ebSVivien Didelot 	/* For the prepare phase, ensure the full set of changes is feasable in
405ba14d9ebSVivien Didelot 	 * one go in order to signal a failure properly. If an operation is not
406ba14d9ebSVivien Didelot 	 * supported, return -EOPNOTSUPP.
407ba14d9ebSVivien Didelot 	 */
408ba14d9ebSVivien Didelot 
4099e8f4a54SJiri Pirko 	switch (obj->id) {
41057d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_FDB:
411648b4a99SJiri Pirko 		err = dsa_slave_port_fdb_add(dev,
412648b4a99SJiri Pirko 					     SWITCHDEV_OBJ_PORT_FDB(obj),
413648b4a99SJiri Pirko 					     trans);
414ba14d9ebSVivien Didelot 		break;
41557d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
416648b4a99SJiri Pirko 		err = dsa_slave_port_vlan_add(dev,
417648b4a99SJiri Pirko 					      SWITCHDEV_OBJ_PORT_VLAN(obj),
418648b4a99SJiri Pirko 					      trans);
41911149536SVivien Didelot 		break;
420ba14d9ebSVivien Didelot 	default:
421ba14d9ebSVivien Didelot 		err = -EOPNOTSUPP;
422ba14d9ebSVivien Didelot 		break;
423ba14d9ebSVivien Didelot 	}
424ba14d9ebSVivien Didelot 
425ba14d9ebSVivien Didelot 	return err;
426ba14d9ebSVivien Didelot }
427ba14d9ebSVivien Didelot 
428ba14d9ebSVivien Didelot static int dsa_slave_port_obj_del(struct net_device *dev,
429648b4a99SJiri Pirko 				  const struct switchdev_obj *obj)
430ba14d9ebSVivien Didelot {
431ba14d9ebSVivien Didelot 	int err;
432ba14d9ebSVivien Didelot 
4339e8f4a54SJiri Pirko 	switch (obj->id) {
43457d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_FDB:
435648b4a99SJiri Pirko 		err = dsa_slave_port_fdb_del(dev,
436648b4a99SJiri Pirko 					     SWITCHDEV_OBJ_PORT_FDB(obj));
437ba14d9ebSVivien Didelot 		break;
43857d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
439648b4a99SJiri Pirko 		err = dsa_slave_port_vlan_del(dev,
440648b4a99SJiri Pirko 					      SWITCHDEV_OBJ_PORT_VLAN(obj));
44111149536SVivien Didelot 		break;
442ba14d9ebSVivien Didelot 	default:
443ba14d9ebSVivien Didelot 		err = -EOPNOTSUPP;
444ba14d9ebSVivien Didelot 		break;
445ba14d9ebSVivien Didelot 	}
446ba14d9ebSVivien Didelot 
447ba14d9ebSVivien Didelot 	return err;
448ba14d9ebSVivien Didelot }
449ba14d9ebSVivien Didelot 
450ba14d9ebSVivien Didelot static int dsa_slave_port_obj_dump(struct net_device *dev,
451648b4a99SJiri Pirko 				   struct switchdev_obj *obj,
452648b4a99SJiri Pirko 				   switchdev_obj_dump_cb_t *cb)
453ba14d9ebSVivien Didelot {
454ba14d9ebSVivien Didelot 	int err;
455ba14d9ebSVivien Didelot 
4569e8f4a54SJiri Pirko 	switch (obj->id) {
45757d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_FDB:
458648b4a99SJiri Pirko 		err = dsa_slave_port_fdb_dump(dev,
459648b4a99SJiri Pirko 					      SWITCHDEV_OBJ_PORT_FDB(obj),
460648b4a99SJiri Pirko 					      cb);
461ba14d9ebSVivien Didelot 		break;
46257d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
463648b4a99SJiri Pirko 		err = dsa_slave_port_vlan_dump(dev,
464648b4a99SJiri Pirko 					       SWITCHDEV_OBJ_PORT_VLAN(obj),
465648b4a99SJiri Pirko 					       cb);
46611149536SVivien Didelot 		break;
467ba14d9ebSVivien Didelot 	default:
468ba14d9ebSVivien Didelot 		err = -EOPNOTSUPP;
469ba14d9ebSVivien Didelot 		break;
470ba14d9ebSVivien Didelot 	}
471ba14d9ebSVivien Didelot 
472ba14d9ebSVivien Didelot 	return err;
473ba14d9ebSVivien Didelot }
474ba14d9ebSVivien Didelot 
475b73adef6SFlorian Fainelli static int dsa_slave_bridge_port_join(struct net_device *dev,
476b73adef6SFlorian Fainelli 				      struct net_device *br)
477b73adef6SFlorian Fainelli {
478b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
479b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
480b73adef6SFlorian Fainelli 	int ret = -EOPNOTSUPP;
481b73adef6SFlorian Fainelli 
482b73adef6SFlorian Fainelli 	p->bridge_dev = br;
483b73adef6SFlorian Fainelli 
484*9d490b4eSVivien Didelot 	if (ds->ops->port_bridge_join)
485*9d490b4eSVivien Didelot 		ret = ds->ops->port_bridge_join(ds, p->port, br);
486b73adef6SFlorian Fainelli 
4876debb68aSVivien Didelot 	return ret == -EOPNOTSUPP ? 0 : ret;
488b73adef6SFlorian Fainelli }
489b73adef6SFlorian Fainelli 
49016bfa702SVivien Didelot static void dsa_slave_bridge_port_leave(struct net_device *dev)
491b73adef6SFlorian Fainelli {
492b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
493b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
494b73adef6SFlorian Fainelli 
495b73adef6SFlorian Fainelli 
496*9d490b4eSVivien Didelot 	if (ds->ops->port_bridge_leave)
497*9d490b4eSVivien Didelot 		ds->ops->port_bridge_leave(ds, p->port);
498b73adef6SFlorian Fainelli 
499b73adef6SFlorian Fainelli 	p->bridge_dev = NULL;
500b73adef6SFlorian Fainelli 
501b73adef6SFlorian Fainelli 	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
502b73adef6SFlorian Fainelli 	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
503b73adef6SFlorian Fainelli 	 */
504*9d490b4eSVivien Didelot 	if (ds->ops->port_stp_state_set)
505*9d490b4eSVivien Didelot 		ds->ops->port_stp_state_set(ds, p->port, BR_STATE_FORWARDING);
506b73adef6SFlorian Fainelli }
507b73adef6SFlorian Fainelli 
508f8e20a9fSScott Feldman static int dsa_slave_port_attr_get(struct net_device *dev,
509f8e20a9fSScott Feldman 				   struct switchdev_attr *attr)
510b73adef6SFlorian Fainelli {
511b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
512b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
513b73adef6SFlorian Fainelli 
514f8e20a9fSScott Feldman 	switch (attr->id) {
5151f868398SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
51642275bd8SScott Feldman 		attr->u.ppid.id_len = sizeof(ds->index);
51742275bd8SScott Feldman 		memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len);
518f8e20a9fSScott Feldman 		break;
519f8e20a9fSScott Feldman 	default:
520f8e20a9fSScott Feldman 		return -EOPNOTSUPP;
521f8e20a9fSScott Feldman 	}
522b73adef6SFlorian Fainelli 
523b73adef6SFlorian Fainelli 	return 0;
524b73adef6SFlorian Fainelli }
525b73adef6SFlorian Fainelli 
52604ff53f9SFlorian Fainelli static inline netdev_tx_t dsa_netpoll_send_skb(struct dsa_slave_priv *p,
52704ff53f9SFlorian Fainelli 					       struct sk_buff *skb)
52804ff53f9SFlorian Fainelli {
52904ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
53004ff53f9SFlorian Fainelli 	if (p->netpoll)
53104ff53f9SFlorian Fainelli 		netpoll_send_skb(p->netpoll, skb);
53204ff53f9SFlorian Fainelli #else
53304ff53f9SFlorian Fainelli 	BUG();
53404ff53f9SFlorian Fainelli #endif
53504ff53f9SFlorian Fainelli 	return NETDEV_TX_OK;
53604ff53f9SFlorian Fainelli }
53704ff53f9SFlorian Fainelli 
5383e8a72d1SFlorian Fainelli static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
5393e8a72d1SFlorian Fainelli {
5403e8a72d1SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
5414ed70ce9SFlorian Fainelli 	struct sk_buff *nskb;
5423e8a72d1SFlorian Fainelli 
5434ed70ce9SFlorian Fainelli 	dev->stats.tx_packets++;
5444ed70ce9SFlorian Fainelli 	dev->stats.tx_bytes += skb->len;
5453e8a72d1SFlorian Fainelli 
5464ed70ce9SFlorian Fainelli 	/* Transmit function may have to reallocate the original SKB */
5474ed70ce9SFlorian Fainelli 	nskb = p->xmit(skb, dev);
5484ed70ce9SFlorian Fainelli 	if (!nskb)
5494ed70ce9SFlorian Fainelli 		return NETDEV_TX_OK;
5505aed85ceSFlorian Fainelli 
55104ff53f9SFlorian Fainelli 	/* SKB for netpoll still need to be mangled with the protocol-specific
55204ff53f9SFlorian Fainelli 	 * tag to be successfully transmitted
55304ff53f9SFlorian Fainelli 	 */
55404ff53f9SFlorian Fainelli 	if (unlikely(netpoll_tx_running(dev)))
55504ff53f9SFlorian Fainelli 		return dsa_netpoll_send_skb(p, nskb);
55604ff53f9SFlorian Fainelli 
5574ed70ce9SFlorian Fainelli 	/* Queue the SKB for transmission on the parent interface, but
5584ed70ce9SFlorian Fainelli 	 * do not modify its EtherType
5594ed70ce9SFlorian Fainelli 	 */
5604ed70ce9SFlorian Fainelli 	nskb->dev = p->parent->dst->master_netdev;
5614ed70ce9SFlorian Fainelli 	dev_queue_xmit(nskb);
5625aed85ceSFlorian Fainelli 
5635aed85ceSFlorian Fainelli 	return NETDEV_TX_OK;
5645aed85ceSFlorian Fainelli }
5655aed85ceSFlorian Fainelli 
56691da11f8SLennert Buytenhek /* ethtool operations *******************************************************/
56791da11f8SLennert Buytenhek static int
56891da11f8SLennert Buytenhek dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
56991da11f8SLennert Buytenhek {
57091da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
57191da11f8SLennert Buytenhek 	int err;
57291da11f8SLennert Buytenhek 
57391da11f8SLennert Buytenhek 	err = -EOPNOTSUPP;
57491da11f8SLennert Buytenhek 	if (p->phy != NULL) {
57591da11f8SLennert Buytenhek 		err = phy_read_status(p->phy);
57691da11f8SLennert Buytenhek 		if (err == 0)
57791da11f8SLennert Buytenhek 			err = phy_ethtool_gset(p->phy, cmd);
57891da11f8SLennert Buytenhek 	}
57991da11f8SLennert Buytenhek 
58091da11f8SLennert Buytenhek 	return err;
58191da11f8SLennert Buytenhek }
58291da11f8SLennert Buytenhek 
58391da11f8SLennert Buytenhek static int
58491da11f8SLennert Buytenhek dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
58591da11f8SLennert Buytenhek {
58691da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
58791da11f8SLennert Buytenhek 
58891da11f8SLennert Buytenhek 	if (p->phy != NULL)
58991da11f8SLennert Buytenhek 		return phy_ethtool_sset(p->phy, cmd);
59091da11f8SLennert Buytenhek 
59191da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
59291da11f8SLennert Buytenhek }
59391da11f8SLennert Buytenhek 
59491da11f8SLennert Buytenhek static void dsa_slave_get_drvinfo(struct net_device *dev,
59591da11f8SLennert Buytenhek 				  struct ethtool_drvinfo *drvinfo)
59691da11f8SLennert Buytenhek {
5977826d43fSJiri Pirko 	strlcpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver));
5987826d43fSJiri Pirko 	strlcpy(drvinfo->version, dsa_driver_version, sizeof(drvinfo->version));
5997826d43fSJiri Pirko 	strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
6007826d43fSJiri Pirko 	strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
60191da11f8SLennert Buytenhek }
60291da11f8SLennert Buytenhek 
6033d762a0fSGuenter Roeck static int dsa_slave_get_regs_len(struct net_device *dev)
6043d762a0fSGuenter Roeck {
6053d762a0fSGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
6063d762a0fSGuenter Roeck 	struct dsa_switch *ds = p->parent;
6073d762a0fSGuenter Roeck 
608*9d490b4eSVivien Didelot 	if (ds->ops->get_regs_len)
609*9d490b4eSVivien Didelot 		return ds->ops->get_regs_len(ds, p->port);
6103d762a0fSGuenter Roeck 
6113d762a0fSGuenter Roeck 	return -EOPNOTSUPP;
6123d762a0fSGuenter Roeck }
6133d762a0fSGuenter Roeck 
6143d762a0fSGuenter Roeck static void
6153d762a0fSGuenter Roeck dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
6163d762a0fSGuenter Roeck {
6173d762a0fSGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
6183d762a0fSGuenter Roeck 	struct dsa_switch *ds = p->parent;
6193d762a0fSGuenter Roeck 
620*9d490b4eSVivien Didelot 	if (ds->ops->get_regs)
621*9d490b4eSVivien Didelot 		ds->ops->get_regs(ds, p->port, regs, _p);
6223d762a0fSGuenter Roeck }
6233d762a0fSGuenter Roeck 
62491da11f8SLennert Buytenhek static int dsa_slave_nway_reset(struct net_device *dev)
62591da11f8SLennert Buytenhek {
62691da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
62791da11f8SLennert Buytenhek 
62891da11f8SLennert Buytenhek 	if (p->phy != NULL)
62991da11f8SLennert Buytenhek 		return genphy_restart_aneg(p->phy);
63091da11f8SLennert Buytenhek 
63191da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
63291da11f8SLennert Buytenhek }
63391da11f8SLennert Buytenhek 
63491da11f8SLennert Buytenhek static u32 dsa_slave_get_link(struct net_device *dev)
63591da11f8SLennert Buytenhek {
63691da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
63791da11f8SLennert Buytenhek 
63891da11f8SLennert Buytenhek 	if (p->phy != NULL) {
63991da11f8SLennert Buytenhek 		genphy_update_link(p->phy);
64091da11f8SLennert Buytenhek 		return p->phy->link;
64191da11f8SLennert Buytenhek 	}
64291da11f8SLennert Buytenhek 
64391da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
64491da11f8SLennert Buytenhek }
64591da11f8SLennert Buytenhek 
6466793abb4SGuenter Roeck static int dsa_slave_get_eeprom_len(struct net_device *dev)
6476793abb4SGuenter Roeck {
6486793abb4SGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
6496793abb4SGuenter Roeck 	struct dsa_switch *ds = p->parent;
6506793abb4SGuenter Roeck 
6510e576044SAndrew Lunn 	if (ds->cd && ds->cd->eeprom_len)
652ff04955cSAndrew Lunn 		return ds->cd->eeprom_len;
6536793abb4SGuenter Roeck 
654*9d490b4eSVivien Didelot 	if (ds->ops->get_eeprom_len)
655*9d490b4eSVivien Didelot 		return ds->ops->get_eeprom_len(ds);
6566793abb4SGuenter Roeck 
6576793abb4SGuenter Roeck 	return 0;
6586793abb4SGuenter Roeck }
6596793abb4SGuenter Roeck 
6606793abb4SGuenter Roeck static int dsa_slave_get_eeprom(struct net_device *dev,
6616793abb4SGuenter Roeck 				struct ethtool_eeprom *eeprom, u8 *data)
6626793abb4SGuenter Roeck {
6636793abb4SGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
6646793abb4SGuenter Roeck 	struct dsa_switch *ds = p->parent;
6656793abb4SGuenter Roeck 
666*9d490b4eSVivien Didelot 	if (ds->ops->get_eeprom)
667*9d490b4eSVivien Didelot 		return ds->ops->get_eeprom(ds, eeprom, data);
6686793abb4SGuenter Roeck 
6696793abb4SGuenter Roeck 	return -EOPNOTSUPP;
6706793abb4SGuenter Roeck }
6716793abb4SGuenter Roeck 
6726793abb4SGuenter Roeck static int dsa_slave_set_eeprom(struct net_device *dev,
6736793abb4SGuenter Roeck 				struct ethtool_eeprom *eeprom, u8 *data)
6746793abb4SGuenter Roeck {
6756793abb4SGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
6766793abb4SGuenter Roeck 	struct dsa_switch *ds = p->parent;
6776793abb4SGuenter Roeck 
678*9d490b4eSVivien Didelot 	if (ds->ops->set_eeprom)
679*9d490b4eSVivien Didelot 		return ds->ops->set_eeprom(ds, eeprom, data);
6806793abb4SGuenter Roeck 
6816793abb4SGuenter Roeck 	return -EOPNOTSUPP;
6826793abb4SGuenter Roeck }
6836793abb4SGuenter Roeck 
68491da11f8SLennert Buytenhek static void dsa_slave_get_strings(struct net_device *dev,
68591da11f8SLennert Buytenhek 				  uint32_t stringset, uint8_t *data)
68691da11f8SLennert Buytenhek {
68791da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
68891da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
68991da11f8SLennert Buytenhek 
69091da11f8SLennert Buytenhek 	if (stringset == ETH_SS_STATS) {
69191da11f8SLennert Buytenhek 		int len = ETH_GSTRING_LEN;
69291da11f8SLennert Buytenhek 
69391da11f8SLennert Buytenhek 		strncpy(data, "tx_packets", len);
69491da11f8SLennert Buytenhek 		strncpy(data + len, "tx_bytes", len);
69591da11f8SLennert Buytenhek 		strncpy(data + 2 * len, "rx_packets", len);
69691da11f8SLennert Buytenhek 		strncpy(data + 3 * len, "rx_bytes", len);
697*9d490b4eSVivien Didelot 		if (ds->ops->get_strings)
698*9d490b4eSVivien Didelot 			ds->ops->get_strings(ds, p->port, data + 4 * len);
69991da11f8SLennert Buytenhek 	}
70091da11f8SLennert Buytenhek }
70191da11f8SLennert Buytenhek 
702badf3adaSFlorian Fainelli static void dsa_cpu_port_get_ethtool_stats(struct net_device *dev,
703badf3adaSFlorian Fainelli 					   struct ethtool_stats *stats,
704badf3adaSFlorian Fainelli 					   uint64_t *data)
705badf3adaSFlorian Fainelli {
706badf3adaSFlorian Fainelli 	struct dsa_switch_tree *dst = dev->dsa_ptr;
707badf3adaSFlorian Fainelli 	struct dsa_switch *ds = dst->ds[0];
708badf3adaSFlorian Fainelli 	s8 cpu_port = dst->cpu_port;
709badf3adaSFlorian Fainelli 	int count = 0;
710badf3adaSFlorian Fainelli 
711badf3adaSFlorian Fainelli 	if (dst->master_ethtool_ops.get_sset_count) {
712badf3adaSFlorian Fainelli 		count = dst->master_ethtool_ops.get_sset_count(dev,
713badf3adaSFlorian Fainelli 							       ETH_SS_STATS);
714badf3adaSFlorian Fainelli 		dst->master_ethtool_ops.get_ethtool_stats(dev, stats, data);
715badf3adaSFlorian Fainelli 	}
716badf3adaSFlorian Fainelli 
717*9d490b4eSVivien Didelot 	if (ds->ops->get_ethtool_stats)
718*9d490b4eSVivien Didelot 		ds->ops->get_ethtool_stats(ds, cpu_port, data + count);
719badf3adaSFlorian Fainelli }
720badf3adaSFlorian Fainelli 
721badf3adaSFlorian Fainelli static int dsa_cpu_port_get_sset_count(struct net_device *dev, int sset)
722badf3adaSFlorian Fainelli {
723badf3adaSFlorian Fainelli 	struct dsa_switch_tree *dst = dev->dsa_ptr;
724badf3adaSFlorian Fainelli 	struct dsa_switch *ds = dst->ds[0];
725badf3adaSFlorian Fainelli 	int count = 0;
726badf3adaSFlorian Fainelli 
727badf3adaSFlorian Fainelli 	if (dst->master_ethtool_ops.get_sset_count)
728badf3adaSFlorian Fainelli 		count += dst->master_ethtool_ops.get_sset_count(dev, sset);
729badf3adaSFlorian Fainelli 
730*9d490b4eSVivien Didelot 	if (sset == ETH_SS_STATS && ds->ops->get_sset_count)
731*9d490b4eSVivien Didelot 		count += ds->ops->get_sset_count(ds);
732badf3adaSFlorian Fainelli 
733badf3adaSFlorian Fainelli 	return count;
734badf3adaSFlorian Fainelli }
735badf3adaSFlorian Fainelli 
736badf3adaSFlorian Fainelli static void dsa_cpu_port_get_strings(struct net_device *dev,
737badf3adaSFlorian Fainelli 				     uint32_t stringset, uint8_t *data)
738badf3adaSFlorian Fainelli {
739badf3adaSFlorian Fainelli 	struct dsa_switch_tree *dst = dev->dsa_ptr;
740badf3adaSFlorian Fainelli 	struct dsa_switch *ds = dst->ds[0];
741badf3adaSFlorian Fainelli 	s8 cpu_port = dst->cpu_port;
742badf3adaSFlorian Fainelli 	int len = ETH_GSTRING_LEN;
743badf3adaSFlorian Fainelli 	int mcount = 0, count;
744badf3adaSFlorian Fainelli 	unsigned int i;
745badf3adaSFlorian Fainelli 	uint8_t pfx[4];
746badf3adaSFlorian Fainelli 	uint8_t *ndata;
747badf3adaSFlorian Fainelli 
748badf3adaSFlorian Fainelli 	snprintf(pfx, sizeof(pfx), "p%.2d", cpu_port);
749badf3adaSFlorian Fainelli 	/* We do not want to be NULL-terminated, since this is a prefix */
750badf3adaSFlorian Fainelli 	pfx[sizeof(pfx) - 1] = '_';
751badf3adaSFlorian Fainelli 
752badf3adaSFlorian Fainelli 	if (dst->master_ethtool_ops.get_sset_count) {
753badf3adaSFlorian Fainelli 		mcount = dst->master_ethtool_ops.get_sset_count(dev,
754badf3adaSFlorian Fainelli 								ETH_SS_STATS);
755badf3adaSFlorian Fainelli 		dst->master_ethtool_ops.get_strings(dev, stringset, data);
756badf3adaSFlorian Fainelli 	}
757badf3adaSFlorian Fainelli 
758*9d490b4eSVivien Didelot 	if (stringset == ETH_SS_STATS && ds->ops->get_strings) {
759badf3adaSFlorian Fainelli 		ndata = data + mcount * len;
760badf3adaSFlorian Fainelli 		/* This function copies ETH_GSTRINGS_LEN bytes, we will mangle
761badf3adaSFlorian Fainelli 		 * the output after to prepend our CPU port prefix we
762badf3adaSFlorian Fainelli 		 * constructed earlier
763badf3adaSFlorian Fainelli 		 */
764*9d490b4eSVivien Didelot 		ds->ops->get_strings(ds, cpu_port, ndata);
765*9d490b4eSVivien Didelot 		count = ds->ops->get_sset_count(ds);
766badf3adaSFlorian Fainelli 		for (i = 0; i < count; i++) {
767badf3adaSFlorian Fainelli 			memmove(ndata + (i * len + sizeof(pfx)),
768badf3adaSFlorian Fainelli 				ndata + i * len, len - sizeof(pfx));
769badf3adaSFlorian Fainelli 			memcpy(ndata + i * len, pfx, sizeof(pfx));
770badf3adaSFlorian Fainelli 		}
771badf3adaSFlorian Fainelli 	}
772badf3adaSFlorian Fainelli }
773badf3adaSFlorian Fainelli 
77491da11f8SLennert Buytenhek static void dsa_slave_get_ethtool_stats(struct net_device *dev,
77591da11f8SLennert Buytenhek 					struct ethtool_stats *stats,
77691da11f8SLennert Buytenhek 					uint64_t *data)
77791da11f8SLennert Buytenhek {
77891da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
77991da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
78091da11f8SLennert Buytenhek 
78146e7b8d8SVivien Didelot 	data[0] = dev->stats.tx_packets;
78246e7b8d8SVivien Didelot 	data[1] = dev->stats.tx_bytes;
78346e7b8d8SVivien Didelot 	data[2] = dev->stats.rx_packets;
78446e7b8d8SVivien Didelot 	data[3] = dev->stats.rx_bytes;
785*9d490b4eSVivien Didelot 	if (ds->ops->get_ethtool_stats)
786*9d490b4eSVivien Didelot 		ds->ops->get_ethtool_stats(ds, p->port, data + 4);
78791da11f8SLennert Buytenhek }
78891da11f8SLennert Buytenhek 
78991da11f8SLennert Buytenhek static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
79091da11f8SLennert Buytenhek {
79191da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
79291da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
79391da11f8SLennert Buytenhek 
79491da11f8SLennert Buytenhek 	if (sset == ETH_SS_STATS) {
79591da11f8SLennert Buytenhek 		int count;
79691da11f8SLennert Buytenhek 
79791da11f8SLennert Buytenhek 		count = 4;
798*9d490b4eSVivien Didelot 		if (ds->ops->get_sset_count)
799*9d490b4eSVivien Didelot 			count += ds->ops->get_sset_count(ds);
80091da11f8SLennert Buytenhek 
80191da11f8SLennert Buytenhek 		return count;
80291da11f8SLennert Buytenhek 	}
80391da11f8SLennert Buytenhek 
80491da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
80591da11f8SLennert Buytenhek }
80691da11f8SLennert Buytenhek 
80719e57c4eSFlorian Fainelli static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
80819e57c4eSFlorian Fainelli {
80919e57c4eSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
81019e57c4eSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
81119e57c4eSFlorian Fainelli 
812*9d490b4eSVivien Didelot 	if (ds->ops->get_wol)
813*9d490b4eSVivien Didelot 		ds->ops->get_wol(ds, p->port, w);
81419e57c4eSFlorian Fainelli }
81519e57c4eSFlorian Fainelli 
81619e57c4eSFlorian Fainelli static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
81719e57c4eSFlorian Fainelli {
81819e57c4eSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
81919e57c4eSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
82019e57c4eSFlorian Fainelli 	int ret = -EOPNOTSUPP;
82119e57c4eSFlorian Fainelli 
822*9d490b4eSVivien Didelot 	if (ds->ops->set_wol)
823*9d490b4eSVivien Didelot 		ret = ds->ops->set_wol(ds, p->port, w);
82419e57c4eSFlorian Fainelli 
82519e57c4eSFlorian Fainelli 	return ret;
82619e57c4eSFlorian Fainelli }
82719e57c4eSFlorian Fainelli 
8287905288fSFlorian Fainelli static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
8297905288fSFlorian Fainelli {
8307905288fSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
8317905288fSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
8327905288fSFlorian Fainelli 	int ret;
8337905288fSFlorian Fainelli 
834*9d490b4eSVivien Didelot 	if (!ds->ops->set_eee)
8357905288fSFlorian Fainelli 		return -EOPNOTSUPP;
8367905288fSFlorian Fainelli 
837*9d490b4eSVivien Didelot 	ret = ds->ops->set_eee(ds, p->port, p->phy, e);
8387905288fSFlorian Fainelli 	if (ret)
8397905288fSFlorian Fainelli 		return ret;
8407905288fSFlorian Fainelli 
8417905288fSFlorian Fainelli 	if (p->phy)
8427905288fSFlorian Fainelli 		ret = phy_ethtool_set_eee(p->phy, e);
8437905288fSFlorian Fainelli 
8447905288fSFlorian Fainelli 	return ret;
8457905288fSFlorian Fainelli }
8467905288fSFlorian Fainelli 
8477905288fSFlorian Fainelli static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
8487905288fSFlorian Fainelli {
8497905288fSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
8507905288fSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
8517905288fSFlorian Fainelli 	int ret;
8527905288fSFlorian Fainelli 
853*9d490b4eSVivien Didelot 	if (!ds->ops->get_eee)
8547905288fSFlorian Fainelli 		return -EOPNOTSUPP;
8557905288fSFlorian Fainelli 
856*9d490b4eSVivien Didelot 	ret = ds->ops->get_eee(ds, p->port, e);
8577905288fSFlorian Fainelli 	if (ret)
8587905288fSFlorian Fainelli 		return ret;
8597905288fSFlorian Fainelli 
8607905288fSFlorian Fainelli 	if (p->phy)
8617905288fSFlorian Fainelli 		ret = phy_ethtool_get_eee(p->phy, e);
8627905288fSFlorian Fainelli 
8637905288fSFlorian Fainelli 	return ret;
8647905288fSFlorian Fainelli }
8657905288fSFlorian Fainelli 
86604ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
86704ff53f9SFlorian Fainelli static int dsa_slave_netpoll_setup(struct net_device *dev,
86804ff53f9SFlorian Fainelli 				   struct netpoll_info *ni)
86904ff53f9SFlorian Fainelli {
87004ff53f9SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
87104ff53f9SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
87204ff53f9SFlorian Fainelli 	struct net_device *master = ds->dst->master_netdev;
87304ff53f9SFlorian Fainelli 	struct netpoll *netpoll;
87404ff53f9SFlorian Fainelli 	int err = 0;
87504ff53f9SFlorian Fainelli 
87604ff53f9SFlorian Fainelli 	netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
87704ff53f9SFlorian Fainelli 	if (!netpoll)
87804ff53f9SFlorian Fainelli 		return -ENOMEM;
87904ff53f9SFlorian Fainelli 
88004ff53f9SFlorian Fainelli 	err = __netpoll_setup(netpoll, master);
88104ff53f9SFlorian Fainelli 	if (err) {
88204ff53f9SFlorian Fainelli 		kfree(netpoll);
88304ff53f9SFlorian Fainelli 		goto out;
88404ff53f9SFlorian Fainelli 	}
88504ff53f9SFlorian Fainelli 
88604ff53f9SFlorian Fainelli 	p->netpoll = netpoll;
88704ff53f9SFlorian Fainelli out:
88804ff53f9SFlorian Fainelli 	return err;
88904ff53f9SFlorian Fainelli }
89004ff53f9SFlorian Fainelli 
89104ff53f9SFlorian Fainelli static void dsa_slave_netpoll_cleanup(struct net_device *dev)
89204ff53f9SFlorian Fainelli {
89304ff53f9SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
89404ff53f9SFlorian Fainelli 	struct netpoll *netpoll = p->netpoll;
89504ff53f9SFlorian Fainelli 
89604ff53f9SFlorian Fainelli 	if (!netpoll)
89704ff53f9SFlorian Fainelli 		return;
89804ff53f9SFlorian Fainelli 
89904ff53f9SFlorian Fainelli 	p->netpoll = NULL;
90004ff53f9SFlorian Fainelli 
90104ff53f9SFlorian Fainelli 	__netpoll_free_async(netpoll);
90204ff53f9SFlorian Fainelli }
90304ff53f9SFlorian Fainelli 
90404ff53f9SFlorian Fainelli static void dsa_slave_poll_controller(struct net_device *dev)
90504ff53f9SFlorian Fainelli {
90604ff53f9SFlorian Fainelli }
90704ff53f9SFlorian Fainelli #endif
90804ff53f9SFlorian Fainelli 
909af42192cSFlorian Fainelli void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops)
910af42192cSFlorian Fainelli {
911af42192cSFlorian Fainelli 	ops->get_sset_count = dsa_cpu_port_get_sset_count;
912af42192cSFlorian Fainelli 	ops->get_ethtool_stats = dsa_cpu_port_get_ethtool_stats;
913af42192cSFlorian Fainelli 	ops->get_strings = dsa_cpu_port_get_strings;
914af42192cSFlorian Fainelli }
915af42192cSFlorian Fainelli 
91691da11f8SLennert Buytenhek static const struct ethtool_ops dsa_slave_ethtool_ops = {
91791da11f8SLennert Buytenhek 	.get_settings		= dsa_slave_get_settings,
91891da11f8SLennert Buytenhek 	.set_settings		= dsa_slave_set_settings,
91991da11f8SLennert Buytenhek 	.get_drvinfo		= dsa_slave_get_drvinfo,
9203d762a0fSGuenter Roeck 	.get_regs_len		= dsa_slave_get_regs_len,
9213d762a0fSGuenter Roeck 	.get_regs		= dsa_slave_get_regs,
92291da11f8SLennert Buytenhek 	.nway_reset		= dsa_slave_nway_reset,
92391da11f8SLennert Buytenhek 	.get_link		= dsa_slave_get_link,
9246793abb4SGuenter Roeck 	.get_eeprom_len		= dsa_slave_get_eeprom_len,
9256793abb4SGuenter Roeck 	.get_eeprom		= dsa_slave_get_eeprom,
9266793abb4SGuenter Roeck 	.set_eeprom		= dsa_slave_set_eeprom,
92791da11f8SLennert Buytenhek 	.get_strings		= dsa_slave_get_strings,
92891da11f8SLennert Buytenhek 	.get_ethtool_stats	= dsa_slave_get_ethtool_stats,
92991da11f8SLennert Buytenhek 	.get_sset_count		= dsa_slave_get_sset_count,
93019e57c4eSFlorian Fainelli 	.set_wol		= dsa_slave_set_wol,
93119e57c4eSFlorian Fainelli 	.get_wol		= dsa_slave_get_wol,
9327905288fSFlorian Fainelli 	.set_eee		= dsa_slave_set_eee,
9337905288fSFlorian Fainelli 	.get_eee		= dsa_slave_get_eee,
93491da11f8SLennert Buytenhek };
93591da11f8SLennert Buytenhek 
9363e8a72d1SFlorian Fainelli static const struct net_device_ops dsa_slave_netdev_ops = {
937d442ad4aSStephen Hemminger 	.ndo_open	 	= dsa_slave_open,
938d442ad4aSStephen Hemminger 	.ndo_stop		= dsa_slave_close,
9393e8a72d1SFlorian Fainelli 	.ndo_start_xmit		= dsa_slave_xmit,
940d442ad4aSStephen Hemminger 	.ndo_change_rx_flags	= dsa_slave_change_rx_flags,
941d442ad4aSStephen Hemminger 	.ndo_set_rx_mode	= dsa_slave_set_rx_mode,
942d442ad4aSStephen Hemminger 	.ndo_set_mac_address	= dsa_slave_set_mac_address,
943ba14d9ebSVivien Didelot 	.ndo_fdb_add		= switchdev_port_fdb_add,
944ba14d9ebSVivien Didelot 	.ndo_fdb_del		= switchdev_port_fdb_del,
945ba14d9ebSVivien Didelot 	.ndo_fdb_dump		= switchdev_port_fdb_dump,
946d442ad4aSStephen Hemminger 	.ndo_do_ioctl		= dsa_slave_ioctl,
947abd2be00SNicolas Dichtel 	.ndo_get_iflink		= dsa_slave_get_iflink,
94804ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
94904ff53f9SFlorian Fainelli 	.ndo_netpoll_setup	= dsa_slave_netpoll_setup,
95004ff53f9SFlorian Fainelli 	.ndo_netpoll_cleanup	= dsa_slave_netpoll_cleanup,
95104ff53f9SFlorian Fainelli 	.ndo_poll_controller	= dsa_slave_poll_controller,
95204ff53f9SFlorian Fainelli #endif
95311149536SVivien Didelot 	.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
95411149536SVivien Didelot 	.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
95511149536SVivien Didelot 	.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
95698237d43SScott Feldman };
95798237d43SScott Feldman 
9589d47c0a2SJiri Pirko static const struct switchdev_ops dsa_slave_switchdev_ops = {
959f8e20a9fSScott Feldman 	.switchdev_port_attr_get	= dsa_slave_port_attr_get,
96035636062SScott Feldman 	.switchdev_port_attr_set	= dsa_slave_port_attr_set,
961ba14d9ebSVivien Didelot 	.switchdev_port_obj_add		= dsa_slave_port_obj_add,
962ba14d9ebSVivien Didelot 	.switchdev_port_obj_del		= dsa_slave_port_obj_del,
963ba14d9ebSVivien Didelot 	.switchdev_port_obj_dump	= dsa_slave_port_obj_dump,
964d442ad4aSStephen Hemminger };
96591da11f8SLennert Buytenhek 
966f37db85dSFlorian Fainelli static struct device_type dsa_type = {
967f37db85dSFlorian Fainelli 	.name	= "dsa",
968f37db85dSFlorian Fainelli };
969f37db85dSFlorian Fainelli 
9700d8bcdd3SFlorian Fainelli static void dsa_slave_adjust_link(struct net_device *dev)
9710d8bcdd3SFlorian Fainelli {
9720d8bcdd3SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
973ec9436baSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
9740d8bcdd3SFlorian Fainelli 	unsigned int status_changed = 0;
9750d8bcdd3SFlorian Fainelli 
9760d8bcdd3SFlorian Fainelli 	if (p->old_link != p->phy->link) {
9770d8bcdd3SFlorian Fainelli 		status_changed = 1;
9780d8bcdd3SFlorian Fainelli 		p->old_link = p->phy->link;
9790d8bcdd3SFlorian Fainelli 	}
9800d8bcdd3SFlorian Fainelli 
9810d8bcdd3SFlorian Fainelli 	if (p->old_duplex != p->phy->duplex) {
9820d8bcdd3SFlorian Fainelli 		status_changed = 1;
9830d8bcdd3SFlorian Fainelli 		p->old_duplex = p->phy->duplex;
9840d8bcdd3SFlorian Fainelli 	}
9850d8bcdd3SFlorian Fainelli 
9860d8bcdd3SFlorian Fainelli 	if (p->old_pause != p->phy->pause) {
9870d8bcdd3SFlorian Fainelli 		status_changed = 1;
9880d8bcdd3SFlorian Fainelli 		p->old_pause = p->phy->pause;
9890d8bcdd3SFlorian Fainelli 	}
9900d8bcdd3SFlorian Fainelli 
991*9d490b4eSVivien Didelot 	if (ds->ops->adjust_link && status_changed)
992*9d490b4eSVivien Didelot 		ds->ops->adjust_link(ds, p->port, p->phy);
993ec9436baSFlorian Fainelli 
9940d8bcdd3SFlorian Fainelli 	if (status_changed)
9950d8bcdd3SFlorian Fainelli 		phy_print_status(p->phy);
9960d8bcdd3SFlorian Fainelli }
9970d8bcdd3SFlorian Fainelli 
998ce31b31cSFlorian Fainelli static int dsa_slave_fixed_link_update(struct net_device *dev,
999ce31b31cSFlorian Fainelli 				       struct fixed_phy_status *status)
1000ce31b31cSFlorian Fainelli {
1001b71be352SAndrew Lunn 	struct dsa_slave_priv *p;
1002b71be352SAndrew Lunn 	struct dsa_switch *ds;
1003ce31b31cSFlorian Fainelli 
1004b71be352SAndrew Lunn 	if (dev) {
1005b71be352SAndrew Lunn 		p = netdev_priv(dev);
1006b71be352SAndrew Lunn 		ds = p->parent;
1007*9d490b4eSVivien Didelot 		if (ds->ops->fixed_link_update)
1008*9d490b4eSVivien Didelot 			ds->ops->fixed_link_update(ds, p->port, status);
1009b71be352SAndrew Lunn 	}
1010ce31b31cSFlorian Fainelli 
1011ce31b31cSFlorian Fainelli 	return 0;
1012ce31b31cSFlorian Fainelli }
1013ce31b31cSFlorian Fainelli 
101491da11f8SLennert Buytenhek /* slave device setup *******************************************************/
1015c305c165SFlorian Fainelli static int dsa_slave_phy_connect(struct dsa_slave_priv *p,
1016cd28a1a9SFlorian Fainelli 				 struct net_device *slave_dev,
1017cd28a1a9SFlorian Fainelli 				 int addr)
1018c305c165SFlorian Fainelli {
1019c305c165SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
1020c305c165SFlorian Fainelli 
10217f854420SAndrew Lunn 	p->phy = mdiobus_get_phy(ds->slave_mii_bus, addr);
1022d25b8e74SRussell King 	if (!p->phy) {
1023d25b8e74SRussell King 		netdev_err(slave_dev, "no phy at %d\n", addr);
1024c305c165SFlorian Fainelli 		return -ENODEV;
1025d25b8e74SRussell King 	}
1026c305c165SFlorian Fainelli 
1027c305c165SFlorian Fainelli 	/* Use already configured phy mode */
1028211c504aSFlorian Fainelli 	if (p->phy_interface == PHY_INTERFACE_MODE_NA)
1029c305c165SFlorian Fainelli 		p->phy_interface = p->phy->interface;
1030c305c165SFlorian Fainelli 	phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
1031c305c165SFlorian Fainelli 			   p->phy_interface);
1032c305c165SFlorian Fainelli 
1033c305c165SFlorian Fainelli 	return 0;
1034c305c165SFlorian Fainelli }
1035c305c165SFlorian Fainelli 
10369697f1cdSFlorian Fainelli static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
10370d8bcdd3SFlorian Fainelli 				struct net_device *slave_dev)
10380d8bcdd3SFlorian Fainelli {
10390d8bcdd3SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
10400d8bcdd3SFlorian Fainelli 	struct device_node *phy_dn, *port_dn;
1041ce31b31cSFlorian Fainelli 	bool phy_is_fixed = false;
10426819563eSFlorian Fainelli 	u32 phy_flags = 0;
104319334920SGuenter Roeck 	int mode, ret;
10440d8bcdd3SFlorian Fainelli 
1045189b0d93SAndrew Lunn 	port_dn = ds->ports[p->port].dn;
104619334920SGuenter Roeck 	mode = of_get_phy_mode(port_dn);
104719334920SGuenter Roeck 	if (mode < 0)
104819334920SGuenter Roeck 		mode = PHY_INTERFACE_MODE_NA;
104919334920SGuenter Roeck 	p->phy_interface = mode;
10500d8bcdd3SFlorian Fainelli 
10510d8bcdd3SFlorian Fainelli 	phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
10520d8bcdd3SFlorian Fainelli 	if (of_phy_is_fixed_link(port_dn)) {
10530d8bcdd3SFlorian Fainelli 		/* In the case of a fixed PHY, the DT node associated
10540d8bcdd3SFlorian Fainelli 		 * to the fixed PHY is the Port DT node
10550d8bcdd3SFlorian Fainelli 		 */
10560d8bcdd3SFlorian Fainelli 		ret = of_phy_register_fixed_link(port_dn);
10570d8bcdd3SFlorian Fainelli 		if (ret) {
1058d25b8e74SRussell King 			netdev_err(slave_dev, "failed to register fixed PHY: %d\n", ret);
10599697f1cdSFlorian Fainelli 			return ret;
10600d8bcdd3SFlorian Fainelli 		}
1061ce31b31cSFlorian Fainelli 		phy_is_fixed = true;
10620d8bcdd3SFlorian Fainelli 		phy_dn = port_dn;
10630d8bcdd3SFlorian Fainelli 	}
10640d8bcdd3SFlorian Fainelli 
1065*9d490b4eSVivien Didelot 	if (ds->ops->get_phy_flags)
1066*9d490b4eSVivien Didelot 		phy_flags = ds->ops->get_phy_flags(ds, p->port);
10676819563eSFlorian Fainelli 
1068cd28a1a9SFlorian Fainelli 	if (phy_dn) {
1069d25b8e74SRussell King 		int phy_id = of_mdio_parse_addr(&slave_dev->dev, phy_dn);
1070d25b8e74SRussell King 
1071cd28a1a9SFlorian Fainelli 		/* If this PHY address is part of phys_mii_mask, which means
1072cd28a1a9SFlorian Fainelli 		 * that we need to divert reads and writes to/from it, then we
1073cd28a1a9SFlorian Fainelli 		 * want to bind this device using the slave MII bus created by
1074cd28a1a9SFlorian Fainelli 		 * DSA to make that happen.
1075cd28a1a9SFlorian Fainelli 		 */
1076d25b8e74SRussell King 		if (!phy_is_fixed && phy_id >= 0 &&
1077d25b8e74SRussell King 		    (ds->phys_mii_mask & (1 << phy_id))) {
1078d25b8e74SRussell King 			ret = dsa_slave_phy_connect(p, slave_dev, phy_id);
1079d25b8e74SRussell King 			if (ret) {
1080d25b8e74SRussell King 				netdev_err(slave_dev, "failed to connect to phy%d: %d\n", phy_id, ret);
1081cd28a1a9SFlorian Fainelli 				return ret;
1082d25b8e74SRussell King 			}
1083cd28a1a9SFlorian Fainelli 		} else {
10840d8bcdd3SFlorian Fainelli 			p->phy = of_phy_connect(slave_dev, phy_dn,
1085cd28a1a9SFlorian Fainelli 						dsa_slave_adjust_link,
1086cd28a1a9SFlorian Fainelli 						phy_flags,
10870d8bcdd3SFlorian Fainelli 						p->phy_interface);
1088cd28a1a9SFlorian Fainelli 		}
1089cd28a1a9SFlorian Fainelli 	}
10900d8bcdd3SFlorian Fainelli 
1091ce31b31cSFlorian Fainelli 	if (p->phy && phy_is_fixed)
1092ce31b31cSFlorian Fainelli 		fixed_phy_set_link_update(p->phy, dsa_slave_fixed_link_update);
1093ce31b31cSFlorian Fainelli 
10940d8bcdd3SFlorian Fainelli 	/* We could not connect to a designated PHY, so use the switch internal
10950d8bcdd3SFlorian Fainelli 	 * MDIO bus instead
10960d8bcdd3SFlorian Fainelli 	 */
1097b31f65fbSAndrew Lunn 	if (!p->phy) {
1098cd28a1a9SFlorian Fainelli 		ret = dsa_slave_phy_connect(p, slave_dev, p->port);
1099d25b8e74SRussell King 		if (ret) {
1100d25b8e74SRussell King 			netdev_err(slave_dev, "failed to connect to port %d: %d\n", p->port, ret);
1101c305c165SFlorian Fainelli 			return ret;
1102d25b8e74SRussell King 		}
11030d8bcdd3SFlorian Fainelli 	}
11049697f1cdSFlorian Fainelli 
11052220943aSAndrew Lunn 	phy_attached_info(p->phy);
11062220943aSAndrew Lunn 
11079697f1cdSFlorian Fainelli 	return 0;
1108b31f65fbSAndrew Lunn }
11090d8bcdd3SFlorian Fainelli 
1110448b4482SAndrew Lunn static struct lock_class_key dsa_slave_netdev_xmit_lock_key;
1111448b4482SAndrew Lunn static void dsa_slave_set_lockdep_class_one(struct net_device *dev,
1112448b4482SAndrew Lunn 					    struct netdev_queue *txq,
1113448b4482SAndrew Lunn 					    void *_unused)
1114448b4482SAndrew Lunn {
1115448b4482SAndrew Lunn 	lockdep_set_class(&txq->_xmit_lock,
1116448b4482SAndrew Lunn 			  &dsa_slave_netdev_xmit_lock_key);
1117448b4482SAndrew Lunn }
1118448b4482SAndrew Lunn 
111924462549SFlorian Fainelli int dsa_slave_suspend(struct net_device *slave_dev)
112024462549SFlorian Fainelli {
112124462549SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(slave_dev);
112224462549SFlorian Fainelli 
112324462549SFlorian Fainelli 	if (p->phy) {
112424462549SFlorian Fainelli 		phy_stop(p->phy);
112524462549SFlorian Fainelli 		p->old_pause = -1;
112624462549SFlorian Fainelli 		p->old_link = -1;
112724462549SFlorian Fainelli 		p->old_duplex = -1;
112824462549SFlorian Fainelli 		phy_suspend(p->phy);
112924462549SFlorian Fainelli 	}
113024462549SFlorian Fainelli 
113124462549SFlorian Fainelli 	return 0;
113224462549SFlorian Fainelli }
113324462549SFlorian Fainelli 
113424462549SFlorian Fainelli int dsa_slave_resume(struct net_device *slave_dev)
113524462549SFlorian Fainelli {
113624462549SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(slave_dev);
113724462549SFlorian Fainelli 
113824462549SFlorian Fainelli 	netif_device_attach(slave_dev);
113924462549SFlorian Fainelli 
114024462549SFlorian Fainelli 	if (p->phy) {
114124462549SFlorian Fainelli 		phy_resume(p->phy);
114224462549SFlorian Fainelli 		phy_start(p->phy);
114324462549SFlorian Fainelli 	}
114424462549SFlorian Fainelli 
114524462549SFlorian Fainelli 	return 0;
114624462549SFlorian Fainelli }
114724462549SFlorian Fainelli 
1148d87d6f44SGuenter Roeck int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
114983c0afaeSAndrew Lunn 		     int port, const char *name)
115091da11f8SLennert Buytenhek {
1151badf3adaSFlorian Fainelli 	struct dsa_switch_tree *dst = ds->dst;
115283c0afaeSAndrew Lunn 	struct net_device *master;
115391da11f8SLennert Buytenhek 	struct net_device *slave_dev;
115491da11f8SLennert Buytenhek 	struct dsa_slave_priv *p;
115591da11f8SLennert Buytenhek 	int ret;
115691da11f8SLennert Buytenhek 
115783c0afaeSAndrew Lunn 	master = ds->dst->master_netdev;
115883c0afaeSAndrew Lunn 	if (ds->master_netdev)
115983c0afaeSAndrew Lunn 		master = ds->master_netdev;
116083c0afaeSAndrew Lunn 
1161c835a677STom Gundersen 	slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name,
1162c835a677STom Gundersen 				 NET_NAME_UNKNOWN, ether_setup);
116391da11f8SLennert Buytenhek 	if (slave_dev == NULL)
1164d87d6f44SGuenter Roeck 		return -ENOMEM;
116591da11f8SLennert Buytenhek 
116691da11f8SLennert Buytenhek 	slave_dev->features = master->vlan_features;
11677ad24ea4SWilfried Klaebe 	slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
11682fcc8005SBjørn Mork 	eth_hw_addr_inherit(slave_dev, master);
11690a5f107bSPhil Sutter 	slave_dev->priv_flags |= IFF_NO_QUEUE;
11703e8a72d1SFlorian Fainelli 	slave_dev->netdev_ops = &dsa_slave_netdev_ops;
11719d47c0a2SJiri Pirko 	slave_dev->switchdev_ops = &dsa_slave_switchdev_ops;
1172f37db85dSFlorian Fainelli 	SET_NETDEV_DEVTYPE(slave_dev, &dsa_type);
1173d442ad4aSStephen Hemminger 
1174448b4482SAndrew Lunn 	netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one,
1175448b4482SAndrew Lunn 				 NULL);
1176448b4482SAndrew Lunn 
117791da11f8SLennert Buytenhek 	SET_NETDEV_DEV(slave_dev, parent);
1178189b0d93SAndrew Lunn 	slave_dev->dev.of_node = ds->ports[port].dn;
117991da11f8SLennert Buytenhek 	slave_dev->vlan_features = master->vlan_features;
118091da11f8SLennert Buytenhek 
118191da11f8SLennert Buytenhek 	p = netdev_priv(slave_dev);
118291da11f8SLennert Buytenhek 	p->parent = ds;
118391da11f8SLennert Buytenhek 	p->port = port;
118439a7f2a4SAndrew Lunn 	p->xmit = dst->tag_ops->xmit;
11855075314eSAlexander Duyck 
11860d8bcdd3SFlorian Fainelli 	p->old_pause = -1;
11870d8bcdd3SFlorian Fainelli 	p->old_link = -1;
11880d8bcdd3SFlorian Fainelli 	p->old_duplex = -1;
11890d8bcdd3SFlorian Fainelli 
1190c8b09808SAndrew Lunn 	ds->ports[port].netdev = slave_dev;
119191da11f8SLennert Buytenhek 	ret = register_netdev(slave_dev);
119291da11f8SLennert Buytenhek 	if (ret) {
1193a2ae6007SJoe Perches 		netdev_err(master, "error %d registering interface %s\n",
1194a2ae6007SJoe Perches 			   ret, slave_dev->name);
1195c8b09808SAndrew Lunn 		ds->ports[port].netdev = NULL;
119691da11f8SLennert Buytenhek 		free_netdev(slave_dev);
1197d87d6f44SGuenter Roeck 		return ret;
119891da11f8SLennert Buytenhek 	}
119991da11f8SLennert Buytenhek 
120091da11f8SLennert Buytenhek 	netif_carrier_off(slave_dev);
120191da11f8SLennert Buytenhek 
12020071f56eSAndrew Lunn 	ret = dsa_slave_phy_setup(p, slave_dev);
12030071f56eSAndrew Lunn 	if (ret) {
12040071f56eSAndrew Lunn 		netdev_err(master, "error %d setting up slave phy\n", ret);
120573dcb556SFlorian Fainelli 		unregister_netdev(slave_dev);
12060071f56eSAndrew Lunn 		free_netdev(slave_dev);
12070071f56eSAndrew Lunn 		return ret;
12080071f56eSAndrew Lunn 	}
12090071f56eSAndrew Lunn 
1210d87d6f44SGuenter Roeck 	return 0;
121191da11f8SLennert Buytenhek }
1212b73adef6SFlorian Fainelli 
1213cda5c15bSNeil Armstrong void dsa_slave_destroy(struct net_device *slave_dev)
1214cda5c15bSNeil Armstrong {
1215cda5c15bSNeil Armstrong 	struct dsa_slave_priv *p = netdev_priv(slave_dev);
1216cda5c15bSNeil Armstrong 
1217cda5c15bSNeil Armstrong 	netif_carrier_off(slave_dev);
1218cda5c15bSNeil Armstrong 	if (p->phy)
1219cda5c15bSNeil Armstrong 		phy_disconnect(p->phy);
1220cda5c15bSNeil Armstrong 	unregister_netdev(slave_dev);
1221cda5c15bSNeil Armstrong 	free_netdev(slave_dev);
1222cda5c15bSNeil Armstrong }
1223cda5c15bSNeil Armstrong 
1224b73adef6SFlorian Fainelli static bool dsa_slave_dev_check(struct net_device *dev)
1225b73adef6SFlorian Fainelli {
1226b73adef6SFlorian Fainelli 	return dev->netdev_ops == &dsa_slave_netdev_ops;
1227b73adef6SFlorian Fainelli }
1228b73adef6SFlorian Fainelli 
12296debb68aSVivien Didelot static int dsa_slave_port_upper_event(struct net_device *dev,
12306debb68aSVivien Didelot 				      unsigned long event, void *ptr)
1231b73adef6SFlorian Fainelli {
12326debb68aSVivien Didelot 	struct netdev_notifier_changeupper_info *info = ptr;
12336debb68aSVivien Didelot 	struct net_device *upper = info->upper_dev;
1234b73adef6SFlorian Fainelli 	int err = 0;
1235b73adef6SFlorian Fainelli 
12366debb68aSVivien Didelot 	switch (event) {
12376debb68aSVivien Didelot 	case NETDEV_CHANGEUPPER:
12386debb68aSVivien Didelot 		if (netif_is_bridge_master(upper)) {
12396debb68aSVivien Didelot 			if (info->linking)
12406debb68aSVivien Didelot 				err = dsa_slave_bridge_port_join(dev, upper);
12416debb68aSVivien Didelot 			else
124216bfa702SVivien Didelot 				dsa_slave_bridge_port_leave(dev);
12436debb68aSVivien Didelot 		}
1244b73adef6SFlorian Fainelli 
12456debb68aSVivien Didelot 		break;
12466debb68aSVivien Didelot 	}
12476debb68aSVivien Didelot 
12486debb68aSVivien Didelot 	return notifier_from_errno(err);
12496debb68aSVivien Didelot }
12506debb68aSVivien Didelot 
12516debb68aSVivien Didelot static int dsa_slave_port_event(struct net_device *dev, unsigned long event,
12526debb68aSVivien Didelot 				void *ptr)
12536debb68aSVivien Didelot {
12546debb68aSVivien Didelot 	switch (event) {
12556debb68aSVivien Didelot 	case NETDEV_CHANGEUPPER:
12566debb68aSVivien Didelot 		return dsa_slave_port_upper_event(dev, event, ptr);
12576debb68aSVivien Didelot 	}
12586debb68aSVivien Didelot 
12596debb68aSVivien Didelot 	return NOTIFY_DONE;
1260b73adef6SFlorian Fainelli }
1261b73adef6SFlorian Fainelli 
1262b73adef6SFlorian Fainelli int dsa_slave_netdevice_event(struct notifier_block *unused,
1263b73adef6SFlorian Fainelli 			      unsigned long event, void *ptr)
1264b73adef6SFlorian Fainelli {
12656debb68aSVivien Didelot 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1266b73adef6SFlorian Fainelli 
12676debb68aSVivien Didelot 	if (dsa_slave_dev_check(dev))
12686debb68aSVivien Didelot 		return dsa_slave_port_event(dev, event, ptr);
1269b73adef6SFlorian Fainelli 
1270b73adef6SFlorian Fainelli 	return NOTIFY_DONE;
1271b73adef6SFlorian Fainelli }
1272