xref: /openbmc/linux/net/dsa/slave.c (revision 4acfee8143b33efa8bda6f03fe1462d545ff8170)
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))
319d490b4eSVivien 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))
419d490b4eSVivien 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 
72*4acfee81SVivien Didelot static void dsa_port_set_stp_state(struct dsa_switch *ds, int port, u8 state)
73*4acfee81SVivien Didelot {
74*4acfee81SVivien Didelot 	if (ds->ops->port_stp_state_set)
75*4acfee81SVivien Didelot 		ds->ops->port_stp_state_set(ds, port, state);
76*4acfee81SVivien Didelot }
77*4acfee81SVivien Didelot 
7891da11f8SLennert Buytenhek static int dsa_slave_open(struct net_device *dev)
7991da11f8SLennert Buytenhek {
80df02c6ffSLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
81e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
82b2f2af21SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
83b73adef6SFlorian Fainelli 	u8 stp_state = dsa_port_is_bridged(p) ?
84b73adef6SFlorian Fainelli 			BR_STATE_BLOCKING : BR_STATE_FORWARDING;
85df02c6ffSLennert Buytenhek 	int err;
86df02c6ffSLennert Buytenhek 
87df02c6ffSLennert Buytenhek 	if (!(master->flags & IFF_UP))
88df02c6ffSLennert Buytenhek 		return -ENETDOWN;
89df02c6ffSLennert Buytenhek 
908feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) {
91a748ee24SJiri Pirko 		err = dev_uc_add(master, dev->dev_addr);
92df02c6ffSLennert Buytenhek 		if (err < 0)
93df02c6ffSLennert Buytenhek 			goto out;
94df02c6ffSLennert Buytenhek 	}
95df02c6ffSLennert Buytenhek 
96df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_ALLMULTI) {
97df02c6ffSLennert Buytenhek 		err = dev_set_allmulti(master, 1);
98df02c6ffSLennert Buytenhek 		if (err < 0)
99df02c6ffSLennert Buytenhek 			goto del_unicast;
100df02c6ffSLennert Buytenhek 	}
101df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_PROMISC) {
102df02c6ffSLennert Buytenhek 		err = dev_set_promiscuity(master, 1);
103df02c6ffSLennert Buytenhek 		if (err < 0)
104df02c6ffSLennert Buytenhek 			goto clear_allmulti;
105df02c6ffSLennert Buytenhek 	}
106df02c6ffSLennert Buytenhek 
1079d490b4eSVivien Didelot 	if (ds->ops->port_enable) {
1089d490b4eSVivien Didelot 		err = ds->ops->port_enable(ds, p->port, p->phy);
109b2f2af21SFlorian Fainelli 		if (err)
110b2f2af21SFlorian Fainelli 			goto clear_promisc;
111b2f2af21SFlorian Fainelli 	}
112b2f2af21SFlorian Fainelli 
113*4acfee81SVivien Didelot 	dsa_port_set_stp_state(ds, p->port, stp_state);
114b73adef6SFlorian Fainelli 
115f7f1de51SFlorian Fainelli 	if (p->phy)
116f7f1de51SFlorian Fainelli 		phy_start(p->phy);
117f7f1de51SFlorian Fainelli 
11891da11f8SLennert Buytenhek 	return 0;
119df02c6ffSLennert Buytenhek 
120b2f2af21SFlorian Fainelli clear_promisc:
121b2f2af21SFlorian Fainelli 	if (dev->flags & IFF_PROMISC)
1224fdeddfeSGilad Ben-Yossef 		dev_set_promiscuity(master, -1);
123df02c6ffSLennert Buytenhek clear_allmulti:
124df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_ALLMULTI)
125df02c6ffSLennert Buytenhek 		dev_set_allmulti(master, -1);
126df02c6ffSLennert Buytenhek del_unicast:
1278feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
128a748ee24SJiri Pirko 		dev_uc_del(master, dev->dev_addr);
129df02c6ffSLennert Buytenhek out:
130df02c6ffSLennert Buytenhek 	return err;
13191da11f8SLennert Buytenhek }
13291da11f8SLennert Buytenhek 
13391da11f8SLennert Buytenhek static int dsa_slave_close(struct net_device *dev)
13491da11f8SLennert Buytenhek {
135df02c6ffSLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
136e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
137b2f2af21SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
138df02c6ffSLennert Buytenhek 
139f7f1de51SFlorian Fainelli 	if (p->phy)
140f7f1de51SFlorian Fainelli 		phy_stop(p->phy);
141f7f1de51SFlorian Fainelli 
142df02c6ffSLennert Buytenhek 	dev_mc_unsync(master, dev);
143a748ee24SJiri Pirko 	dev_uc_unsync(master, dev);
144df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_ALLMULTI)
145df02c6ffSLennert Buytenhek 		dev_set_allmulti(master, -1);
146df02c6ffSLennert Buytenhek 	if (dev->flags & IFF_PROMISC)
147df02c6ffSLennert Buytenhek 		dev_set_promiscuity(master, -1);
148df02c6ffSLennert Buytenhek 
1498feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
150a748ee24SJiri Pirko 		dev_uc_del(master, dev->dev_addr);
151df02c6ffSLennert Buytenhek 
1529d490b4eSVivien Didelot 	if (ds->ops->port_disable)
1539d490b4eSVivien Didelot 		ds->ops->port_disable(ds, p->port, p->phy);
154b2f2af21SFlorian Fainelli 
155*4acfee81SVivien Didelot 	dsa_port_set_stp_state(ds, p->port, BR_STATE_DISABLED);
156b73adef6SFlorian Fainelli 
15791da11f8SLennert Buytenhek 	return 0;
15891da11f8SLennert Buytenhek }
15991da11f8SLennert Buytenhek 
16091da11f8SLennert Buytenhek static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
16191da11f8SLennert Buytenhek {
16291da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
163e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
16491da11f8SLennert Buytenhek 
16591da11f8SLennert Buytenhek 	if (change & IFF_ALLMULTI)
16691da11f8SLennert Buytenhek 		dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
16791da11f8SLennert Buytenhek 	if (change & IFF_PROMISC)
16891da11f8SLennert Buytenhek 		dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1);
16991da11f8SLennert Buytenhek }
17091da11f8SLennert Buytenhek 
17191da11f8SLennert Buytenhek static void dsa_slave_set_rx_mode(struct net_device *dev)
17291da11f8SLennert Buytenhek {
17391da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
174e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
17591da11f8SLennert Buytenhek 
17691da11f8SLennert Buytenhek 	dev_mc_sync(master, dev);
177a748ee24SJiri Pirko 	dev_uc_sync(master, dev);
17891da11f8SLennert Buytenhek }
17991da11f8SLennert Buytenhek 
180df02c6ffSLennert Buytenhek static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
18191da11f8SLennert Buytenhek {
182df02c6ffSLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
183e84665c9SLennert Buytenhek 	struct net_device *master = p->parent->dst->master_netdev;
184df02c6ffSLennert Buytenhek 	struct sockaddr *addr = a;
185df02c6ffSLennert Buytenhek 	int err;
186df02c6ffSLennert Buytenhek 
187df02c6ffSLennert Buytenhek 	if (!is_valid_ether_addr(addr->sa_data))
188df02c6ffSLennert Buytenhek 		return -EADDRNOTAVAIL;
189df02c6ffSLennert Buytenhek 
190df02c6ffSLennert Buytenhek 	if (!(dev->flags & IFF_UP))
191df02c6ffSLennert Buytenhek 		goto out;
192df02c6ffSLennert Buytenhek 
1938feedbb4SJoe Perches 	if (!ether_addr_equal(addr->sa_data, master->dev_addr)) {
194a748ee24SJiri Pirko 		err = dev_uc_add(master, addr->sa_data);
195df02c6ffSLennert Buytenhek 		if (err < 0)
196df02c6ffSLennert Buytenhek 			return err;
197df02c6ffSLennert Buytenhek 	}
198df02c6ffSLennert Buytenhek 
1998feedbb4SJoe Perches 	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
200a748ee24SJiri Pirko 		dev_uc_del(master, dev->dev_addr);
201df02c6ffSLennert Buytenhek 
202df02c6ffSLennert Buytenhek out:
203d08f161aSJoe Perches 	ether_addr_copy(dev->dev_addr, addr->sa_data);
20491da11f8SLennert Buytenhek 
20591da11f8SLennert Buytenhek 	return 0;
20691da11f8SLennert Buytenhek }
20791da11f8SLennert Buytenhek 
20811149536SVivien Didelot static int dsa_slave_port_vlan_add(struct net_device *dev,
2098f24f309SJiri Pirko 				   const struct switchdev_obj_port_vlan *vlan,
210f8db8348SJiri Pirko 				   struct switchdev_trans *trans)
21111149536SVivien Didelot {
21211149536SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
21311149536SVivien Didelot 	struct dsa_switch *ds = p->parent;
21411149536SVivien Didelot 
21579a62eb2SJiri Pirko 	if (switchdev_trans_ph_prepare(trans)) {
2169d490b4eSVivien Didelot 		if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
21711149536SVivien Didelot 			return -EOPNOTSUPP;
21811149536SVivien Didelot 
2199d490b4eSVivien Didelot 		return ds->ops->port_vlan_prepare(ds, p->port, vlan, trans);
22011149536SVivien Didelot 	}
22111149536SVivien Didelot 
2229d490b4eSVivien Didelot 	ds->ops->port_vlan_add(ds, p->port, vlan, trans);
2234d5770b3SVivien Didelot 
22411149536SVivien Didelot 	return 0;
22511149536SVivien Didelot }
22611149536SVivien Didelot 
22711149536SVivien Didelot static int dsa_slave_port_vlan_del(struct net_device *dev,
2288f24f309SJiri Pirko 				   const struct switchdev_obj_port_vlan *vlan)
22911149536SVivien Didelot {
23011149536SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
23111149536SVivien Didelot 	struct dsa_switch *ds = p->parent;
23211149536SVivien Didelot 
2339d490b4eSVivien Didelot 	if (!ds->ops->port_vlan_del)
23411149536SVivien Didelot 		return -EOPNOTSUPP;
23511149536SVivien Didelot 
2369d490b4eSVivien Didelot 	return ds->ops->port_vlan_del(ds, p->port, vlan);
23711149536SVivien Didelot }
23811149536SVivien Didelot 
23911149536SVivien Didelot static int dsa_slave_port_vlan_dump(struct net_device *dev,
2408f24f309SJiri Pirko 				    struct switchdev_obj_port_vlan *vlan,
241648b4a99SJiri Pirko 				    switchdev_obj_dump_cb_t *cb)
24211149536SVivien Didelot {
24311149536SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
24411149536SVivien Didelot 	struct dsa_switch *ds = p->parent;
24511149536SVivien Didelot 
2469d490b4eSVivien Didelot 	if (ds->ops->port_vlan_dump)
2479d490b4eSVivien Didelot 		return ds->ops->port_vlan_dump(ds, p->port, vlan, cb);
24865aebfc0SVivien Didelot 
24911149536SVivien Didelot 	return -EOPNOTSUPP;
25011149536SVivien Didelot }
25111149536SVivien Didelot 
252ba14d9ebSVivien Didelot static int dsa_slave_port_fdb_add(struct net_device *dev,
25352ba57cfSJiri Pirko 				  const struct switchdev_obj_port_fdb *fdb,
254f8db8348SJiri Pirko 				  struct switchdev_trans *trans)
255cdf09697SDavid S. Miller {
256cdf09697SDavid S. Miller 	struct dsa_slave_priv *p = netdev_priv(dev);
257cdf09697SDavid S. Miller 	struct dsa_switch *ds = p->parent;
258146a3206SVivien Didelot 
2598497aa61SVivien Didelot 	if (switchdev_trans_ph_prepare(trans)) {
2609d490b4eSVivien Didelot 		if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add)
261146a3206SVivien Didelot 			return -EOPNOTSUPP;
262cdf09697SDavid S. Miller 
2639d490b4eSVivien Didelot 		return ds->ops->port_fdb_prepare(ds, p->port, fdb, trans);
2648497aa61SVivien Didelot 	}
265cdf09697SDavid S. Miller 
2669d490b4eSVivien Didelot 	ds->ops->port_fdb_add(ds, p->port, fdb, trans);
2678497aa61SVivien Didelot 
2688497aa61SVivien Didelot 	return 0;
269cdf09697SDavid S. Miller }
270cdf09697SDavid S. Miller 
271ba14d9ebSVivien Didelot static int dsa_slave_port_fdb_del(struct net_device *dev,
27252ba57cfSJiri Pirko 				  const struct switchdev_obj_port_fdb *fdb)
273cdf09697SDavid S. Miller {
274cdf09697SDavid S. Miller 	struct dsa_slave_priv *p = netdev_priv(dev);
275cdf09697SDavid S. Miller 	struct dsa_switch *ds = p->parent;
276cdf09697SDavid S. Miller 	int ret = -EOPNOTSUPP;
277cdf09697SDavid S. Miller 
2789d490b4eSVivien Didelot 	if (ds->ops->port_fdb_del)
2799d490b4eSVivien Didelot 		ret = ds->ops->port_fdb_del(ds, p->port, fdb);
280cdf09697SDavid S. Miller 
281cdf09697SDavid S. Miller 	return ret;
282cdf09697SDavid S. Miller }
283cdf09697SDavid S. Miller 
284ba14d9ebSVivien Didelot static int dsa_slave_port_fdb_dump(struct net_device *dev,
28552ba57cfSJiri Pirko 				   struct switchdev_obj_port_fdb *fdb,
286648b4a99SJiri Pirko 				   switchdev_obj_dump_cb_t *cb)
287cdf09697SDavid S. Miller {
288cdf09697SDavid S. Miller 	struct dsa_slave_priv *p = netdev_priv(dev);
289cdf09697SDavid S. Miller 	struct dsa_switch *ds = p->parent;
290cdf09697SDavid S. Miller 
2919d490b4eSVivien Didelot 	if (ds->ops->port_fdb_dump)
2929d490b4eSVivien Didelot 		return ds->ops->port_fdb_dump(ds, p->port, fdb, cb);
293ea70ba98SVivien Didelot 
294cdf09697SDavid S. Miller 	return -EOPNOTSUPP;
295cdf09697SDavid S. Miller }
296cdf09697SDavid S. Miller 
2978df30255SVivien Didelot static int dsa_slave_port_mdb_add(struct net_device *dev,
2988df30255SVivien Didelot 				  const struct switchdev_obj_port_mdb *mdb,
2998df30255SVivien Didelot 				  struct switchdev_trans *trans)
3008df30255SVivien Didelot {
3018df30255SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
3028df30255SVivien Didelot 	struct dsa_switch *ds = p->parent;
3038df30255SVivien Didelot 
3048df30255SVivien Didelot 	if (switchdev_trans_ph_prepare(trans)) {
3058df30255SVivien Didelot 		if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
3068df30255SVivien Didelot 			return -EOPNOTSUPP;
3078df30255SVivien Didelot 
3088df30255SVivien Didelot 		return ds->ops->port_mdb_prepare(ds, p->port, mdb, trans);
3098df30255SVivien Didelot 	}
3108df30255SVivien Didelot 
3118df30255SVivien Didelot 	ds->ops->port_mdb_add(ds, p->port, mdb, trans);
3128df30255SVivien Didelot 
3138df30255SVivien Didelot 	return 0;
3148df30255SVivien Didelot }
3158df30255SVivien Didelot 
3168df30255SVivien Didelot static int dsa_slave_port_mdb_del(struct net_device *dev,
3178df30255SVivien Didelot 				  const struct switchdev_obj_port_mdb *mdb)
3188df30255SVivien Didelot {
3198df30255SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
3208df30255SVivien Didelot 	struct dsa_switch *ds = p->parent;
3218df30255SVivien Didelot 
3228df30255SVivien Didelot 	if (ds->ops->port_mdb_del)
3238df30255SVivien Didelot 		return ds->ops->port_mdb_del(ds, p->port, mdb);
3248df30255SVivien Didelot 
3258df30255SVivien Didelot 	return -EOPNOTSUPP;
3268df30255SVivien Didelot }
3278df30255SVivien Didelot 
3288df30255SVivien Didelot static int dsa_slave_port_mdb_dump(struct net_device *dev,
3298df30255SVivien Didelot 				   struct switchdev_obj_port_mdb *mdb,
3308df30255SVivien Didelot 				   switchdev_obj_dump_cb_t *cb)
3318df30255SVivien Didelot {
3328df30255SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
3338df30255SVivien Didelot 	struct dsa_switch *ds = p->parent;
3348df30255SVivien Didelot 
3358df30255SVivien Didelot 	if (ds->ops->port_mdb_dump)
3368df30255SVivien Didelot 		return ds->ops->port_mdb_dump(ds, p->port, mdb, cb);
3378df30255SVivien Didelot 
3388df30255SVivien Didelot 	return -EOPNOTSUPP;
3398df30255SVivien Didelot }
3408df30255SVivien Didelot 
34191da11f8SLennert Buytenhek static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
34291da11f8SLennert Buytenhek {
34391da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
34491da11f8SLennert Buytenhek 
34591da11f8SLennert Buytenhek 	if (p->phy != NULL)
34628b04113SRichard Cochran 		return phy_mii_ioctl(p->phy, ifr, cmd);
34791da11f8SLennert Buytenhek 
34891da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
34991da11f8SLennert Buytenhek }
35091da11f8SLennert Buytenhek 
35143c44a9fSVivien Didelot static int dsa_slave_stp_state_set(struct net_device *dev,
35243c44a9fSVivien Didelot 				   const struct switchdev_attr *attr,
35343c44a9fSVivien Didelot 				   struct switchdev_trans *trans)
354b73adef6SFlorian Fainelli {
355b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
356b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
357b73adef6SFlorian Fainelli 
35843c44a9fSVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
3599d490b4eSVivien Didelot 		return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP;
360b73adef6SFlorian Fainelli 
361*4acfee81SVivien Didelot 	dsa_port_set_stp_state(ds, p->port, attr->u.stp_state);
36243c44a9fSVivien Didelot 
36343c44a9fSVivien Didelot 	return 0;
364b73adef6SFlorian Fainelli }
365b73adef6SFlorian Fainelli 
366fb2dabadSVivien Didelot static int dsa_slave_vlan_filtering(struct net_device *dev,
367fb2dabadSVivien Didelot 				    const struct switchdev_attr *attr,
368fb2dabadSVivien Didelot 				    struct switchdev_trans *trans)
369fb2dabadSVivien Didelot {
370fb2dabadSVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
371fb2dabadSVivien Didelot 	struct dsa_switch *ds = p->parent;
372fb2dabadSVivien Didelot 
373fb2dabadSVivien Didelot 	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
374fb2dabadSVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
375fb2dabadSVivien Didelot 		return 0;
376fb2dabadSVivien Didelot 
3779d490b4eSVivien Didelot 	if (ds->ops->port_vlan_filtering)
3789d490b4eSVivien Didelot 		return ds->ops->port_vlan_filtering(ds, p->port,
379fb2dabadSVivien Didelot 						    attr->u.vlan_filtering);
380fb2dabadSVivien Didelot 
381fb2dabadSVivien Didelot 	return 0;
382fb2dabadSVivien Didelot }
383fb2dabadSVivien Didelot 
38434a79f63SVivien Didelot static int dsa_fastest_ageing_time(struct dsa_switch *ds,
38534a79f63SVivien Didelot 				   unsigned int ageing_time)
38634a79f63SVivien Didelot {
38734a79f63SVivien Didelot 	int i;
38834a79f63SVivien Didelot 
38934a79f63SVivien Didelot 	for (i = 0; i < DSA_MAX_PORTS; ++i) {
39034a79f63SVivien Didelot 		struct dsa_port *dp = &ds->ports[i];
39134a79f63SVivien Didelot 
39234a79f63SVivien Didelot 		if (dp && dp->ageing_time && dp->ageing_time < ageing_time)
39334a79f63SVivien Didelot 			ageing_time = dp->ageing_time;
39434a79f63SVivien Didelot 	}
39534a79f63SVivien Didelot 
39634a79f63SVivien Didelot 	return ageing_time;
39734a79f63SVivien Didelot }
39834a79f63SVivien Didelot 
39934a79f63SVivien Didelot static int dsa_slave_ageing_time(struct net_device *dev,
40034a79f63SVivien Didelot 				 const struct switchdev_attr *attr,
40134a79f63SVivien Didelot 				 struct switchdev_trans *trans)
40234a79f63SVivien Didelot {
40334a79f63SVivien Didelot 	struct dsa_slave_priv *p = netdev_priv(dev);
40434a79f63SVivien Didelot 	struct dsa_switch *ds = p->parent;
40534a79f63SVivien Didelot 	unsigned long ageing_jiffies = clock_t_to_jiffies(attr->u.ageing_time);
40634a79f63SVivien Didelot 	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
40734a79f63SVivien Didelot 
40834a79f63SVivien Didelot 	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
40934a79f63SVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
41034a79f63SVivien Didelot 		return 0;
41134a79f63SVivien Didelot 
41234a79f63SVivien Didelot 	/* Keep the fastest ageing time in case of multiple bridges */
41334a79f63SVivien Didelot 	ds->ports[p->port].ageing_time = ageing_time;
41434a79f63SVivien Didelot 	ageing_time = dsa_fastest_ageing_time(ds, ageing_time);
41534a79f63SVivien Didelot 
4169d490b4eSVivien Didelot 	if (ds->ops->set_ageing_time)
4179d490b4eSVivien Didelot 		return ds->ops->set_ageing_time(ds, ageing_time);
41834a79f63SVivien Didelot 
41934a79f63SVivien Didelot 	return 0;
42034a79f63SVivien Didelot }
42134a79f63SVivien Didelot 
42235636062SScott Feldman static int dsa_slave_port_attr_set(struct net_device *dev,
423f7fadf30SJiri Pirko 				   const struct switchdev_attr *attr,
4247ea6eb3fSJiri Pirko 				   struct switchdev_trans *trans)
42535636062SScott Feldman {
426b8d866acSVivien Didelot 	int ret;
42735636062SScott Feldman 
42835636062SScott Feldman 	switch (attr->id) {
4291f868398SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
43043c44a9fSVivien Didelot 		ret = dsa_slave_stp_state_set(dev, attr, trans);
43135636062SScott Feldman 		break;
432fb2dabadSVivien Didelot 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
433fb2dabadSVivien Didelot 		ret = dsa_slave_vlan_filtering(dev, attr, trans);
434fb2dabadSVivien Didelot 		break;
43534a79f63SVivien Didelot 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
43634a79f63SVivien Didelot 		ret = dsa_slave_ageing_time(dev, attr, trans);
43734a79f63SVivien Didelot 		break;
43835636062SScott Feldman 	default:
43935636062SScott Feldman 		ret = -EOPNOTSUPP;
44035636062SScott Feldman 		break;
44135636062SScott Feldman 	}
44235636062SScott Feldman 
44335636062SScott Feldman 	return ret;
44435636062SScott Feldman }
44535636062SScott Feldman 
446ba14d9ebSVivien Didelot static int dsa_slave_port_obj_add(struct net_device *dev,
447648b4a99SJiri Pirko 				  const struct switchdev_obj *obj,
4487ea6eb3fSJiri Pirko 				  struct switchdev_trans *trans)
449ba14d9ebSVivien Didelot {
450ba14d9ebSVivien Didelot 	int err;
451ba14d9ebSVivien Didelot 
452ba14d9ebSVivien Didelot 	/* For the prepare phase, ensure the full set of changes is feasable in
453ba14d9ebSVivien Didelot 	 * one go in order to signal a failure properly. If an operation is not
454ba14d9ebSVivien Didelot 	 * supported, return -EOPNOTSUPP.
455ba14d9ebSVivien Didelot 	 */
456ba14d9ebSVivien Didelot 
4579e8f4a54SJiri Pirko 	switch (obj->id) {
45857d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_FDB:
459648b4a99SJiri Pirko 		err = dsa_slave_port_fdb_add(dev,
460648b4a99SJiri Pirko 					     SWITCHDEV_OBJ_PORT_FDB(obj),
461648b4a99SJiri Pirko 					     trans);
462ba14d9ebSVivien Didelot 		break;
4638df30255SVivien Didelot 	case SWITCHDEV_OBJ_ID_PORT_MDB:
4648df30255SVivien Didelot 		err = dsa_slave_port_mdb_add(dev, SWITCHDEV_OBJ_PORT_MDB(obj),
4658df30255SVivien Didelot 					     trans);
4668df30255SVivien Didelot 		break;
46757d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
468648b4a99SJiri Pirko 		err = dsa_slave_port_vlan_add(dev,
469648b4a99SJiri Pirko 					      SWITCHDEV_OBJ_PORT_VLAN(obj),
470648b4a99SJiri Pirko 					      trans);
47111149536SVivien Didelot 		break;
472ba14d9ebSVivien Didelot 	default:
473ba14d9ebSVivien Didelot 		err = -EOPNOTSUPP;
474ba14d9ebSVivien Didelot 		break;
475ba14d9ebSVivien Didelot 	}
476ba14d9ebSVivien Didelot 
477ba14d9ebSVivien Didelot 	return err;
478ba14d9ebSVivien Didelot }
479ba14d9ebSVivien Didelot 
480ba14d9ebSVivien Didelot static int dsa_slave_port_obj_del(struct net_device *dev,
481648b4a99SJiri Pirko 				  const struct switchdev_obj *obj)
482ba14d9ebSVivien Didelot {
483ba14d9ebSVivien Didelot 	int err;
484ba14d9ebSVivien Didelot 
4859e8f4a54SJiri Pirko 	switch (obj->id) {
48657d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_FDB:
487648b4a99SJiri Pirko 		err = dsa_slave_port_fdb_del(dev,
488648b4a99SJiri Pirko 					     SWITCHDEV_OBJ_PORT_FDB(obj));
489ba14d9ebSVivien Didelot 		break;
4908df30255SVivien Didelot 	case SWITCHDEV_OBJ_ID_PORT_MDB:
4918df30255SVivien Didelot 		err = dsa_slave_port_mdb_del(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
4928df30255SVivien Didelot 		break;
49357d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
494648b4a99SJiri Pirko 		err = dsa_slave_port_vlan_del(dev,
495648b4a99SJiri Pirko 					      SWITCHDEV_OBJ_PORT_VLAN(obj));
49611149536SVivien Didelot 		break;
497ba14d9ebSVivien Didelot 	default:
498ba14d9ebSVivien Didelot 		err = -EOPNOTSUPP;
499ba14d9ebSVivien Didelot 		break;
500ba14d9ebSVivien Didelot 	}
501ba14d9ebSVivien Didelot 
502ba14d9ebSVivien Didelot 	return err;
503ba14d9ebSVivien Didelot }
504ba14d9ebSVivien Didelot 
505ba14d9ebSVivien Didelot static int dsa_slave_port_obj_dump(struct net_device *dev,
506648b4a99SJiri Pirko 				   struct switchdev_obj *obj,
507648b4a99SJiri Pirko 				   switchdev_obj_dump_cb_t *cb)
508ba14d9ebSVivien Didelot {
509ba14d9ebSVivien Didelot 	int err;
510ba14d9ebSVivien Didelot 
5119e8f4a54SJiri Pirko 	switch (obj->id) {
51257d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_FDB:
513648b4a99SJiri Pirko 		err = dsa_slave_port_fdb_dump(dev,
514648b4a99SJiri Pirko 					      SWITCHDEV_OBJ_PORT_FDB(obj),
515648b4a99SJiri Pirko 					      cb);
516ba14d9ebSVivien Didelot 		break;
5178df30255SVivien Didelot 	case SWITCHDEV_OBJ_ID_PORT_MDB:
5188df30255SVivien Didelot 		err = dsa_slave_port_mdb_dump(dev, SWITCHDEV_OBJ_PORT_MDB(obj),
5198df30255SVivien Didelot 					      cb);
5208df30255SVivien Didelot 		break;
52157d80838SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
522648b4a99SJiri Pirko 		err = dsa_slave_port_vlan_dump(dev,
523648b4a99SJiri Pirko 					       SWITCHDEV_OBJ_PORT_VLAN(obj),
524648b4a99SJiri Pirko 					       cb);
52511149536SVivien Didelot 		break;
526ba14d9ebSVivien Didelot 	default:
527ba14d9ebSVivien Didelot 		err = -EOPNOTSUPP;
528ba14d9ebSVivien Didelot 		break;
529ba14d9ebSVivien Didelot 	}
530ba14d9ebSVivien Didelot 
531ba14d9ebSVivien Didelot 	return err;
532ba14d9ebSVivien Didelot }
533ba14d9ebSVivien Didelot 
534b73adef6SFlorian Fainelli static int dsa_slave_bridge_port_join(struct net_device *dev,
535b73adef6SFlorian Fainelli 				      struct net_device *br)
536b73adef6SFlorian Fainelli {
537b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
538b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
539b73adef6SFlorian Fainelli 	int ret = -EOPNOTSUPP;
540b73adef6SFlorian Fainelli 
541b73adef6SFlorian Fainelli 	p->bridge_dev = br;
542b73adef6SFlorian Fainelli 
5439d490b4eSVivien Didelot 	if (ds->ops->port_bridge_join)
5449d490b4eSVivien Didelot 		ret = ds->ops->port_bridge_join(ds, p->port, br);
545b73adef6SFlorian Fainelli 
5466debb68aSVivien Didelot 	return ret == -EOPNOTSUPP ? 0 : ret;
547b73adef6SFlorian Fainelli }
548b73adef6SFlorian Fainelli 
54916bfa702SVivien Didelot static void dsa_slave_bridge_port_leave(struct net_device *dev)
550b73adef6SFlorian Fainelli {
551b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
552b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
553b73adef6SFlorian Fainelli 
554b73adef6SFlorian Fainelli 
5559d490b4eSVivien Didelot 	if (ds->ops->port_bridge_leave)
5569d490b4eSVivien Didelot 		ds->ops->port_bridge_leave(ds, p->port);
557b73adef6SFlorian Fainelli 
558b73adef6SFlorian Fainelli 	p->bridge_dev = NULL;
559b73adef6SFlorian Fainelli 
560b73adef6SFlorian Fainelli 	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
561b73adef6SFlorian Fainelli 	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
562b73adef6SFlorian Fainelli 	 */
563*4acfee81SVivien Didelot 	dsa_port_set_stp_state(ds, p->port, BR_STATE_FORWARDING);
564b73adef6SFlorian Fainelli }
565b73adef6SFlorian Fainelli 
566f8e20a9fSScott Feldman static int dsa_slave_port_attr_get(struct net_device *dev,
567f8e20a9fSScott Feldman 				   struct switchdev_attr *attr)
568b73adef6SFlorian Fainelli {
569b73adef6SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
570b73adef6SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
571b73adef6SFlorian Fainelli 
572f8e20a9fSScott Feldman 	switch (attr->id) {
5731f868398SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
57442275bd8SScott Feldman 		attr->u.ppid.id_len = sizeof(ds->index);
57542275bd8SScott Feldman 		memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len);
576f8e20a9fSScott Feldman 		break;
577f8e20a9fSScott Feldman 	default:
578f8e20a9fSScott Feldman 		return -EOPNOTSUPP;
579f8e20a9fSScott Feldman 	}
580b73adef6SFlorian Fainelli 
581b73adef6SFlorian Fainelli 	return 0;
582b73adef6SFlorian Fainelli }
583b73adef6SFlorian Fainelli 
58404ff53f9SFlorian Fainelli static inline netdev_tx_t dsa_netpoll_send_skb(struct dsa_slave_priv *p,
58504ff53f9SFlorian Fainelli 					       struct sk_buff *skb)
58604ff53f9SFlorian Fainelli {
58704ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
58804ff53f9SFlorian Fainelli 	if (p->netpoll)
58904ff53f9SFlorian Fainelli 		netpoll_send_skb(p->netpoll, skb);
59004ff53f9SFlorian Fainelli #else
59104ff53f9SFlorian Fainelli 	BUG();
59204ff53f9SFlorian Fainelli #endif
59304ff53f9SFlorian Fainelli 	return NETDEV_TX_OK;
59404ff53f9SFlorian Fainelli }
59504ff53f9SFlorian Fainelli 
5963e8a72d1SFlorian Fainelli static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
5973e8a72d1SFlorian Fainelli {
5983e8a72d1SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
5994ed70ce9SFlorian Fainelli 	struct sk_buff *nskb;
6003e8a72d1SFlorian Fainelli 
6014ed70ce9SFlorian Fainelli 	dev->stats.tx_packets++;
6024ed70ce9SFlorian Fainelli 	dev->stats.tx_bytes += skb->len;
6033e8a72d1SFlorian Fainelli 
6044ed70ce9SFlorian Fainelli 	/* Transmit function may have to reallocate the original SKB */
6054ed70ce9SFlorian Fainelli 	nskb = p->xmit(skb, dev);
6064ed70ce9SFlorian Fainelli 	if (!nskb)
6074ed70ce9SFlorian Fainelli 		return NETDEV_TX_OK;
6085aed85ceSFlorian Fainelli 
60904ff53f9SFlorian Fainelli 	/* SKB for netpoll still need to be mangled with the protocol-specific
61004ff53f9SFlorian Fainelli 	 * tag to be successfully transmitted
61104ff53f9SFlorian Fainelli 	 */
61204ff53f9SFlorian Fainelli 	if (unlikely(netpoll_tx_running(dev)))
61304ff53f9SFlorian Fainelli 		return dsa_netpoll_send_skb(p, nskb);
61404ff53f9SFlorian Fainelli 
6154ed70ce9SFlorian Fainelli 	/* Queue the SKB for transmission on the parent interface, but
6164ed70ce9SFlorian Fainelli 	 * do not modify its EtherType
6174ed70ce9SFlorian Fainelli 	 */
6184ed70ce9SFlorian Fainelli 	nskb->dev = p->parent->dst->master_netdev;
6194ed70ce9SFlorian Fainelli 	dev_queue_xmit(nskb);
6205aed85ceSFlorian Fainelli 
6215aed85ceSFlorian Fainelli 	return NETDEV_TX_OK;
6225aed85ceSFlorian Fainelli }
6235aed85ceSFlorian Fainelli 
62491da11f8SLennert Buytenhek /* ethtool operations *******************************************************/
62591da11f8SLennert Buytenhek static int
62691da11f8SLennert Buytenhek dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
62791da11f8SLennert Buytenhek {
62891da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
62991da11f8SLennert Buytenhek 	int err;
63091da11f8SLennert Buytenhek 
63191da11f8SLennert Buytenhek 	err = -EOPNOTSUPP;
63291da11f8SLennert Buytenhek 	if (p->phy != NULL) {
63391da11f8SLennert Buytenhek 		err = phy_read_status(p->phy);
63491da11f8SLennert Buytenhek 		if (err == 0)
63591da11f8SLennert Buytenhek 			err = phy_ethtool_gset(p->phy, cmd);
63691da11f8SLennert Buytenhek 	}
63791da11f8SLennert Buytenhek 
63891da11f8SLennert Buytenhek 	return err;
63991da11f8SLennert Buytenhek }
64091da11f8SLennert Buytenhek 
64191da11f8SLennert Buytenhek static int
64291da11f8SLennert Buytenhek dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
64391da11f8SLennert Buytenhek {
64491da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
64591da11f8SLennert Buytenhek 
64691da11f8SLennert Buytenhek 	if (p->phy != NULL)
64791da11f8SLennert Buytenhek 		return phy_ethtool_sset(p->phy, cmd);
64891da11f8SLennert Buytenhek 
64991da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
65091da11f8SLennert Buytenhek }
65191da11f8SLennert Buytenhek 
65291da11f8SLennert Buytenhek static void dsa_slave_get_drvinfo(struct net_device *dev,
65391da11f8SLennert Buytenhek 				  struct ethtool_drvinfo *drvinfo)
65491da11f8SLennert Buytenhek {
6557826d43fSJiri Pirko 	strlcpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver));
6567826d43fSJiri Pirko 	strlcpy(drvinfo->version, dsa_driver_version, sizeof(drvinfo->version));
6577826d43fSJiri Pirko 	strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
6587826d43fSJiri Pirko 	strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
65991da11f8SLennert Buytenhek }
66091da11f8SLennert Buytenhek 
6613d762a0fSGuenter Roeck static int dsa_slave_get_regs_len(struct net_device *dev)
6623d762a0fSGuenter Roeck {
6633d762a0fSGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
6643d762a0fSGuenter Roeck 	struct dsa_switch *ds = p->parent;
6653d762a0fSGuenter Roeck 
6669d490b4eSVivien Didelot 	if (ds->ops->get_regs_len)
6679d490b4eSVivien Didelot 		return ds->ops->get_regs_len(ds, p->port);
6683d762a0fSGuenter Roeck 
6693d762a0fSGuenter Roeck 	return -EOPNOTSUPP;
6703d762a0fSGuenter Roeck }
6713d762a0fSGuenter Roeck 
6723d762a0fSGuenter Roeck static void
6733d762a0fSGuenter Roeck dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
6743d762a0fSGuenter Roeck {
6753d762a0fSGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
6763d762a0fSGuenter Roeck 	struct dsa_switch *ds = p->parent;
6773d762a0fSGuenter Roeck 
6789d490b4eSVivien Didelot 	if (ds->ops->get_regs)
6799d490b4eSVivien Didelot 		ds->ops->get_regs(ds, p->port, regs, _p);
6803d762a0fSGuenter Roeck }
6813d762a0fSGuenter Roeck 
68291da11f8SLennert Buytenhek static int dsa_slave_nway_reset(struct net_device *dev)
68391da11f8SLennert Buytenhek {
68491da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
68591da11f8SLennert Buytenhek 
68691da11f8SLennert Buytenhek 	if (p->phy != NULL)
68791da11f8SLennert Buytenhek 		return genphy_restart_aneg(p->phy);
68891da11f8SLennert Buytenhek 
68991da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
69091da11f8SLennert Buytenhek }
69191da11f8SLennert Buytenhek 
69291da11f8SLennert Buytenhek static u32 dsa_slave_get_link(struct net_device *dev)
69391da11f8SLennert Buytenhek {
69491da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
69591da11f8SLennert Buytenhek 
69691da11f8SLennert Buytenhek 	if (p->phy != NULL) {
69791da11f8SLennert Buytenhek 		genphy_update_link(p->phy);
69891da11f8SLennert Buytenhek 		return p->phy->link;
69991da11f8SLennert Buytenhek 	}
70091da11f8SLennert Buytenhek 
70191da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
70291da11f8SLennert Buytenhek }
70391da11f8SLennert Buytenhek 
7046793abb4SGuenter Roeck static int dsa_slave_get_eeprom_len(struct net_device *dev)
7056793abb4SGuenter Roeck {
7066793abb4SGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
7076793abb4SGuenter Roeck 	struct dsa_switch *ds = p->parent;
7086793abb4SGuenter Roeck 
7090e576044SAndrew Lunn 	if (ds->cd && ds->cd->eeprom_len)
710ff04955cSAndrew Lunn 		return ds->cd->eeprom_len;
7116793abb4SGuenter Roeck 
7129d490b4eSVivien Didelot 	if (ds->ops->get_eeprom_len)
7139d490b4eSVivien Didelot 		return ds->ops->get_eeprom_len(ds);
7146793abb4SGuenter Roeck 
7156793abb4SGuenter Roeck 	return 0;
7166793abb4SGuenter Roeck }
7176793abb4SGuenter Roeck 
7186793abb4SGuenter Roeck static int dsa_slave_get_eeprom(struct net_device *dev,
7196793abb4SGuenter Roeck 				struct ethtool_eeprom *eeprom, u8 *data)
7206793abb4SGuenter Roeck {
7216793abb4SGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
7226793abb4SGuenter Roeck 	struct dsa_switch *ds = p->parent;
7236793abb4SGuenter Roeck 
7249d490b4eSVivien Didelot 	if (ds->ops->get_eeprom)
7259d490b4eSVivien Didelot 		return ds->ops->get_eeprom(ds, eeprom, data);
7266793abb4SGuenter Roeck 
7276793abb4SGuenter Roeck 	return -EOPNOTSUPP;
7286793abb4SGuenter Roeck }
7296793abb4SGuenter Roeck 
7306793abb4SGuenter Roeck static int dsa_slave_set_eeprom(struct net_device *dev,
7316793abb4SGuenter Roeck 				struct ethtool_eeprom *eeprom, u8 *data)
7326793abb4SGuenter Roeck {
7336793abb4SGuenter Roeck 	struct dsa_slave_priv *p = netdev_priv(dev);
7346793abb4SGuenter Roeck 	struct dsa_switch *ds = p->parent;
7356793abb4SGuenter Roeck 
7369d490b4eSVivien Didelot 	if (ds->ops->set_eeprom)
7379d490b4eSVivien Didelot 		return ds->ops->set_eeprom(ds, eeprom, data);
7386793abb4SGuenter Roeck 
7396793abb4SGuenter Roeck 	return -EOPNOTSUPP;
7406793abb4SGuenter Roeck }
7416793abb4SGuenter Roeck 
74291da11f8SLennert Buytenhek static void dsa_slave_get_strings(struct net_device *dev,
74391da11f8SLennert Buytenhek 				  uint32_t stringset, uint8_t *data)
74491da11f8SLennert Buytenhek {
74591da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
74691da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
74791da11f8SLennert Buytenhek 
74891da11f8SLennert Buytenhek 	if (stringset == ETH_SS_STATS) {
74991da11f8SLennert Buytenhek 		int len = ETH_GSTRING_LEN;
75091da11f8SLennert Buytenhek 
75191da11f8SLennert Buytenhek 		strncpy(data, "tx_packets", len);
75291da11f8SLennert Buytenhek 		strncpy(data + len, "tx_bytes", len);
75391da11f8SLennert Buytenhek 		strncpy(data + 2 * len, "rx_packets", len);
75491da11f8SLennert Buytenhek 		strncpy(data + 3 * len, "rx_bytes", len);
7559d490b4eSVivien Didelot 		if (ds->ops->get_strings)
7569d490b4eSVivien Didelot 			ds->ops->get_strings(ds, p->port, data + 4 * len);
75791da11f8SLennert Buytenhek 	}
75891da11f8SLennert Buytenhek }
75991da11f8SLennert Buytenhek 
760badf3adaSFlorian Fainelli static void dsa_cpu_port_get_ethtool_stats(struct net_device *dev,
761badf3adaSFlorian Fainelli 					   struct ethtool_stats *stats,
762badf3adaSFlorian Fainelli 					   uint64_t *data)
763badf3adaSFlorian Fainelli {
764badf3adaSFlorian Fainelli 	struct dsa_switch_tree *dst = dev->dsa_ptr;
765badf3adaSFlorian Fainelli 	struct dsa_switch *ds = dst->ds[0];
766badf3adaSFlorian Fainelli 	s8 cpu_port = dst->cpu_port;
767badf3adaSFlorian Fainelli 	int count = 0;
768badf3adaSFlorian Fainelli 
769badf3adaSFlorian Fainelli 	if (dst->master_ethtool_ops.get_sset_count) {
770badf3adaSFlorian Fainelli 		count = dst->master_ethtool_ops.get_sset_count(dev,
771badf3adaSFlorian Fainelli 							       ETH_SS_STATS);
772badf3adaSFlorian Fainelli 		dst->master_ethtool_ops.get_ethtool_stats(dev, stats, data);
773badf3adaSFlorian Fainelli 	}
774badf3adaSFlorian Fainelli 
7759d490b4eSVivien Didelot 	if (ds->ops->get_ethtool_stats)
7769d490b4eSVivien Didelot 		ds->ops->get_ethtool_stats(ds, cpu_port, data + count);
777badf3adaSFlorian Fainelli }
778badf3adaSFlorian Fainelli 
779badf3adaSFlorian Fainelli static int dsa_cpu_port_get_sset_count(struct net_device *dev, int sset)
780badf3adaSFlorian Fainelli {
781badf3adaSFlorian Fainelli 	struct dsa_switch_tree *dst = dev->dsa_ptr;
782badf3adaSFlorian Fainelli 	struct dsa_switch *ds = dst->ds[0];
783badf3adaSFlorian Fainelli 	int count = 0;
784badf3adaSFlorian Fainelli 
785badf3adaSFlorian Fainelli 	if (dst->master_ethtool_ops.get_sset_count)
786badf3adaSFlorian Fainelli 		count += dst->master_ethtool_ops.get_sset_count(dev, sset);
787badf3adaSFlorian Fainelli 
7889d490b4eSVivien Didelot 	if (sset == ETH_SS_STATS && ds->ops->get_sset_count)
7899d490b4eSVivien Didelot 		count += ds->ops->get_sset_count(ds);
790badf3adaSFlorian Fainelli 
791badf3adaSFlorian Fainelli 	return count;
792badf3adaSFlorian Fainelli }
793badf3adaSFlorian Fainelli 
794badf3adaSFlorian Fainelli static void dsa_cpu_port_get_strings(struct net_device *dev,
795badf3adaSFlorian Fainelli 				     uint32_t stringset, uint8_t *data)
796badf3adaSFlorian Fainelli {
797badf3adaSFlorian Fainelli 	struct dsa_switch_tree *dst = dev->dsa_ptr;
798badf3adaSFlorian Fainelli 	struct dsa_switch *ds = dst->ds[0];
799badf3adaSFlorian Fainelli 	s8 cpu_port = dst->cpu_port;
800badf3adaSFlorian Fainelli 	int len = ETH_GSTRING_LEN;
801badf3adaSFlorian Fainelli 	int mcount = 0, count;
802badf3adaSFlorian Fainelli 	unsigned int i;
803badf3adaSFlorian Fainelli 	uint8_t pfx[4];
804badf3adaSFlorian Fainelli 	uint8_t *ndata;
805badf3adaSFlorian Fainelli 
806badf3adaSFlorian Fainelli 	snprintf(pfx, sizeof(pfx), "p%.2d", cpu_port);
807badf3adaSFlorian Fainelli 	/* We do not want to be NULL-terminated, since this is a prefix */
808badf3adaSFlorian Fainelli 	pfx[sizeof(pfx) - 1] = '_';
809badf3adaSFlorian Fainelli 
810badf3adaSFlorian Fainelli 	if (dst->master_ethtool_ops.get_sset_count) {
811badf3adaSFlorian Fainelli 		mcount = dst->master_ethtool_ops.get_sset_count(dev,
812badf3adaSFlorian Fainelli 								ETH_SS_STATS);
813badf3adaSFlorian Fainelli 		dst->master_ethtool_ops.get_strings(dev, stringset, data);
814badf3adaSFlorian Fainelli 	}
815badf3adaSFlorian Fainelli 
8169d490b4eSVivien Didelot 	if (stringset == ETH_SS_STATS && ds->ops->get_strings) {
817badf3adaSFlorian Fainelli 		ndata = data + mcount * len;
818badf3adaSFlorian Fainelli 		/* This function copies ETH_GSTRINGS_LEN bytes, we will mangle
819badf3adaSFlorian Fainelli 		 * the output after to prepend our CPU port prefix we
820badf3adaSFlorian Fainelli 		 * constructed earlier
821badf3adaSFlorian Fainelli 		 */
8229d490b4eSVivien Didelot 		ds->ops->get_strings(ds, cpu_port, ndata);
8239d490b4eSVivien Didelot 		count = ds->ops->get_sset_count(ds);
824badf3adaSFlorian Fainelli 		for (i = 0; i < count; i++) {
825badf3adaSFlorian Fainelli 			memmove(ndata + (i * len + sizeof(pfx)),
826badf3adaSFlorian Fainelli 				ndata + i * len, len - sizeof(pfx));
827badf3adaSFlorian Fainelli 			memcpy(ndata + i * len, pfx, sizeof(pfx));
828badf3adaSFlorian Fainelli 		}
829badf3adaSFlorian Fainelli 	}
830badf3adaSFlorian Fainelli }
831badf3adaSFlorian Fainelli 
83291da11f8SLennert Buytenhek static void dsa_slave_get_ethtool_stats(struct net_device *dev,
83391da11f8SLennert Buytenhek 					struct ethtool_stats *stats,
83491da11f8SLennert Buytenhek 					uint64_t *data)
83591da11f8SLennert Buytenhek {
83691da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
83791da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
83891da11f8SLennert Buytenhek 
83946e7b8d8SVivien Didelot 	data[0] = dev->stats.tx_packets;
84046e7b8d8SVivien Didelot 	data[1] = dev->stats.tx_bytes;
84146e7b8d8SVivien Didelot 	data[2] = dev->stats.rx_packets;
84246e7b8d8SVivien Didelot 	data[3] = dev->stats.rx_bytes;
8439d490b4eSVivien Didelot 	if (ds->ops->get_ethtool_stats)
8449d490b4eSVivien Didelot 		ds->ops->get_ethtool_stats(ds, p->port, data + 4);
84591da11f8SLennert Buytenhek }
84691da11f8SLennert Buytenhek 
84791da11f8SLennert Buytenhek static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
84891da11f8SLennert Buytenhek {
84991da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
85091da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
85191da11f8SLennert Buytenhek 
85291da11f8SLennert Buytenhek 	if (sset == ETH_SS_STATS) {
85391da11f8SLennert Buytenhek 		int count;
85491da11f8SLennert Buytenhek 
85591da11f8SLennert Buytenhek 		count = 4;
8569d490b4eSVivien Didelot 		if (ds->ops->get_sset_count)
8579d490b4eSVivien Didelot 			count += ds->ops->get_sset_count(ds);
85891da11f8SLennert Buytenhek 
85991da11f8SLennert Buytenhek 		return count;
86091da11f8SLennert Buytenhek 	}
86191da11f8SLennert Buytenhek 
86291da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
86391da11f8SLennert Buytenhek }
86491da11f8SLennert Buytenhek 
86519e57c4eSFlorian Fainelli static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
86619e57c4eSFlorian Fainelli {
86719e57c4eSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
86819e57c4eSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
86919e57c4eSFlorian Fainelli 
8709d490b4eSVivien Didelot 	if (ds->ops->get_wol)
8719d490b4eSVivien Didelot 		ds->ops->get_wol(ds, p->port, w);
87219e57c4eSFlorian Fainelli }
87319e57c4eSFlorian Fainelli 
87419e57c4eSFlorian Fainelli static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
87519e57c4eSFlorian Fainelli {
87619e57c4eSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
87719e57c4eSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
87819e57c4eSFlorian Fainelli 	int ret = -EOPNOTSUPP;
87919e57c4eSFlorian Fainelli 
8809d490b4eSVivien Didelot 	if (ds->ops->set_wol)
8819d490b4eSVivien Didelot 		ret = ds->ops->set_wol(ds, p->port, w);
88219e57c4eSFlorian Fainelli 
88319e57c4eSFlorian Fainelli 	return ret;
88419e57c4eSFlorian Fainelli }
88519e57c4eSFlorian Fainelli 
8867905288fSFlorian Fainelli static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
8877905288fSFlorian Fainelli {
8887905288fSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
8897905288fSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
8907905288fSFlorian Fainelli 	int ret;
8917905288fSFlorian Fainelli 
8929d490b4eSVivien Didelot 	if (!ds->ops->set_eee)
8937905288fSFlorian Fainelli 		return -EOPNOTSUPP;
8947905288fSFlorian Fainelli 
8959d490b4eSVivien Didelot 	ret = ds->ops->set_eee(ds, p->port, p->phy, e);
8967905288fSFlorian Fainelli 	if (ret)
8977905288fSFlorian Fainelli 		return ret;
8987905288fSFlorian Fainelli 
8997905288fSFlorian Fainelli 	if (p->phy)
9007905288fSFlorian Fainelli 		ret = phy_ethtool_set_eee(p->phy, e);
9017905288fSFlorian Fainelli 
9027905288fSFlorian Fainelli 	return ret;
9037905288fSFlorian Fainelli }
9047905288fSFlorian Fainelli 
9057905288fSFlorian Fainelli static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
9067905288fSFlorian Fainelli {
9077905288fSFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
9087905288fSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
9097905288fSFlorian Fainelli 	int ret;
9107905288fSFlorian Fainelli 
9119d490b4eSVivien Didelot 	if (!ds->ops->get_eee)
9127905288fSFlorian Fainelli 		return -EOPNOTSUPP;
9137905288fSFlorian Fainelli 
9149d490b4eSVivien Didelot 	ret = ds->ops->get_eee(ds, p->port, e);
9157905288fSFlorian Fainelli 	if (ret)
9167905288fSFlorian Fainelli 		return ret;
9177905288fSFlorian Fainelli 
9187905288fSFlorian Fainelli 	if (p->phy)
9197905288fSFlorian Fainelli 		ret = phy_ethtool_get_eee(p->phy, e);
9207905288fSFlorian Fainelli 
9217905288fSFlorian Fainelli 	return ret;
9227905288fSFlorian Fainelli }
9237905288fSFlorian Fainelli 
92404ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
92504ff53f9SFlorian Fainelli static int dsa_slave_netpoll_setup(struct net_device *dev,
92604ff53f9SFlorian Fainelli 				   struct netpoll_info *ni)
92704ff53f9SFlorian Fainelli {
92804ff53f9SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
92904ff53f9SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
93004ff53f9SFlorian Fainelli 	struct net_device *master = ds->dst->master_netdev;
93104ff53f9SFlorian Fainelli 	struct netpoll *netpoll;
93204ff53f9SFlorian Fainelli 	int err = 0;
93304ff53f9SFlorian Fainelli 
93404ff53f9SFlorian Fainelli 	netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
93504ff53f9SFlorian Fainelli 	if (!netpoll)
93604ff53f9SFlorian Fainelli 		return -ENOMEM;
93704ff53f9SFlorian Fainelli 
93804ff53f9SFlorian Fainelli 	err = __netpoll_setup(netpoll, master);
93904ff53f9SFlorian Fainelli 	if (err) {
94004ff53f9SFlorian Fainelli 		kfree(netpoll);
94104ff53f9SFlorian Fainelli 		goto out;
94204ff53f9SFlorian Fainelli 	}
94304ff53f9SFlorian Fainelli 
94404ff53f9SFlorian Fainelli 	p->netpoll = netpoll;
94504ff53f9SFlorian Fainelli out:
94604ff53f9SFlorian Fainelli 	return err;
94704ff53f9SFlorian Fainelli }
94804ff53f9SFlorian Fainelli 
94904ff53f9SFlorian Fainelli static void dsa_slave_netpoll_cleanup(struct net_device *dev)
95004ff53f9SFlorian Fainelli {
95104ff53f9SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
95204ff53f9SFlorian Fainelli 	struct netpoll *netpoll = p->netpoll;
95304ff53f9SFlorian Fainelli 
95404ff53f9SFlorian Fainelli 	if (!netpoll)
95504ff53f9SFlorian Fainelli 		return;
95604ff53f9SFlorian Fainelli 
95704ff53f9SFlorian Fainelli 	p->netpoll = NULL;
95804ff53f9SFlorian Fainelli 
95904ff53f9SFlorian Fainelli 	__netpoll_free_async(netpoll);
96004ff53f9SFlorian Fainelli }
96104ff53f9SFlorian Fainelli 
96204ff53f9SFlorian Fainelli static void dsa_slave_poll_controller(struct net_device *dev)
96304ff53f9SFlorian Fainelli {
96404ff53f9SFlorian Fainelli }
96504ff53f9SFlorian Fainelli #endif
96604ff53f9SFlorian Fainelli 
967af42192cSFlorian Fainelli void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops)
968af42192cSFlorian Fainelli {
969af42192cSFlorian Fainelli 	ops->get_sset_count = dsa_cpu_port_get_sset_count;
970af42192cSFlorian Fainelli 	ops->get_ethtool_stats = dsa_cpu_port_get_ethtool_stats;
971af42192cSFlorian Fainelli 	ops->get_strings = dsa_cpu_port_get_strings;
972af42192cSFlorian Fainelli }
973af42192cSFlorian Fainelli 
97491da11f8SLennert Buytenhek static const struct ethtool_ops dsa_slave_ethtool_ops = {
97591da11f8SLennert Buytenhek 	.get_settings		= dsa_slave_get_settings,
97691da11f8SLennert Buytenhek 	.set_settings		= dsa_slave_set_settings,
97791da11f8SLennert Buytenhek 	.get_drvinfo		= dsa_slave_get_drvinfo,
9783d762a0fSGuenter Roeck 	.get_regs_len		= dsa_slave_get_regs_len,
9793d762a0fSGuenter Roeck 	.get_regs		= dsa_slave_get_regs,
98091da11f8SLennert Buytenhek 	.nway_reset		= dsa_slave_nway_reset,
98191da11f8SLennert Buytenhek 	.get_link		= dsa_slave_get_link,
9826793abb4SGuenter Roeck 	.get_eeprom_len		= dsa_slave_get_eeprom_len,
9836793abb4SGuenter Roeck 	.get_eeprom		= dsa_slave_get_eeprom,
9846793abb4SGuenter Roeck 	.set_eeprom		= dsa_slave_set_eeprom,
98591da11f8SLennert Buytenhek 	.get_strings		= dsa_slave_get_strings,
98691da11f8SLennert Buytenhek 	.get_ethtool_stats	= dsa_slave_get_ethtool_stats,
98791da11f8SLennert Buytenhek 	.get_sset_count		= dsa_slave_get_sset_count,
98819e57c4eSFlorian Fainelli 	.set_wol		= dsa_slave_set_wol,
98919e57c4eSFlorian Fainelli 	.get_wol		= dsa_slave_get_wol,
9907905288fSFlorian Fainelli 	.set_eee		= dsa_slave_set_eee,
9917905288fSFlorian Fainelli 	.get_eee		= dsa_slave_get_eee,
99291da11f8SLennert Buytenhek };
99391da11f8SLennert Buytenhek 
9943e8a72d1SFlorian Fainelli static const struct net_device_ops dsa_slave_netdev_ops = {
995d442ad4aSStephen Hemminger 	.ndo_open	 	= dsa_slave_open,
996d442ad4aSStephen Hemminger 	.ndo_stop		= dsa_slave_close,
9973e8a72d1SFlorian Fainelli 	.ndo_start_xmit		= dsa_slave_xmit,
998d442ad4aSStephen Hemminger 	.ndo_change_rx_flags	= dsa_slave_change_rx_flags,
999d442ad4aSStephen Hemminger 	.ndo_set_rx_mode	= dsa_slave_set_rx_mode,
1000d442ad4aSStephen Hemminger 	.ndo_set_mac_address	= dsa_slave_set_mac_address,
1001ba14d9ebSVivien Didelot 	.ndo_fdb_add		= switchdev_port_fdb_add,
1002ba14d9ebSVivien Didelot 	.ndo_fdb_del		= switchdev_port_fdb_del,
1003ba14d9ebSVivien Didelot 	.ndo_fdb_dump		= switchdev_port_fdb_dump,
1004d442ad4aSStephen Hemminger 	.ndo_do_ioctl		= dsa_slave_ioctl,
1005abd2be00SNicolas Dichtel 	.ndo_get_iflink		= dsa_slave_get_iflink,
100604ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
100704ff53f9SFlorian Fainelli 	.ndo_netpoll_setup	= dsa_slave_netpoll_setup,
100804ff53f9SFlorian Fainelli 	.ndo_netpoll_cleanup	= dsa_slave_netpoll_cleanup,
100904ff53f9SFlorian Fainelli 	.ndo_poll_controller	= dsa_slave_poll_controller,
101004ff53f9SFlorian Fainelli #endif
101111149536SVivien Didelot 	.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
101211149536SVivien Didelot 	.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
101311149536SVivien Didelot 	.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
101498237d43SScott Feldman };
101598237d43SScott Feldman 
10169d47c0a2SJiri Pirko static const struct switchdev_ops dsa_slave_switchdev_ops = {
1017f8e20a9fSScott Feldman 	.switchdev_port_attr_get	= dsa_slave_port_attr_get,
101835636062SScott Feldman 	.switchdev_port_attr_set	= dsa_slave_port_attr_set,
1019ba14d9ebSVivien Didelot 	.switchdev_port_obj_add		= dsa_slave_port_obj_add,
1020ba14d9ebSVivien Didelot 	.switchdev_port_obj_del		= dsa_slave_port_obj_del,
1021ba14d9ebSVivien Didelot 	.switchdev_port_obj_dump	= dsa_slave_port_obj_dump,
1022d442ad4aSStephen Hemminger };
102391da11f8SLennert Buytenhek 
1024f37db85dSFlorian Fainelli static struct device_type dsa_type = {
1025f37db85dSFlorian Fainelli 	.name	= "dsa",
1026f37db85dSFlorian Fainelli };
1027f37db85dSFlorian Fainelli 
10280d8bcdd3SFlorian Fainelli static void dsa_slave_adjust_link(struct net_device *dev)
10290d8bcdd3SFlorian Fainelli {
10300d8bcdd3SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(dev);
1031ec9436baSFlorian Fainelli 	struct dsa_switch *ds = p->parent;
10320d8bcdd3SFlorian Fainelli 	unsigned int status_changed = 0;
10330d8bcdd3SFlorian Fainelli 
10340d8bcdd3SFlorian Fainelli 	if (p->old_link != p->phy->link) {
10350d8bcdd3SFlorian Fainelli 		status_changed = 1;
10360d8bcdd3SFlorian Fainelli 		p->old_link = p->phy->link;
10370d8bcdd3SFlorian Fainelli 	}
10380d8bcdd3SFlorian Fainelli 
10390d8bcdd3SFlorian Fainelli 	if (p->old_duplex != p->phy->duplex) {
10400d8bcdd3SFlorian Fainelli 		status_changed = 1;
10410d8bcdd3SFlorian Fainelli 		p->old_duplex = p->phy->duplex;
10420d8bcdd3SFlorian Fainelli 	}
10430d8bcdd3SFlorian Fainelli 
10440d8bcdd3SFlorian Fainelli 	if (p->old_pause != p->phy->pause) {
10450d8bcdd3SFlorian Fainelli 		status_changed = 1;
10460d8bcdd3SFlorian Fainelli 		p->old_pause = p->phy->pause;
10470d8bcdd3SFlorian Fainelli 	}
10480d8bcdd3SFlorian Fainelli 
10499d490b4eSVivien Didelot 	if (ds->ops->adjust_link && status_changed)
10509d490b4eSVivien Didelot 		ds->ops->adjust_link(ds, p->port, p->phy);
1051ec9436baSFlorian Fainelli 
10520d8bcdd3SFlorian Fainelli 	if (status_changed)
10530d8bcdd3SFlorian Fainelli 		phy_print_status(p->phy);
10540d8bcdd3SFlorian Fainelli }
10550d8bcdd3SFlorian Fainelli 
1056ce31b31cSFlorian Fainelli static int dsa_slave_fixed_link_update(struct net_device *dev,
1057ce31b31cSFlorian Fainelli 				       struct fixed_phy_status *status)
1058ce31b31cSFlorian Fainelli {
1059b71be352SAndrew Lunn 	struct dsa_slave_priv *p;
1060b71be352SAndrew Lunn 	struct dsa_switch *ds;
1061ce31b31cSFlorian Fainelli 
1062b71be352SAndrew Lunn 	if (dev) {
1063b71be352SAndrew Lunn 		p = netdev_priv(dev);
1064b71be352SAndrew Lunn 		ds = p->parent;
10659d490b4eSVivien Didelot 		if (ds->ops->fixed_link_update)
10669d490b4eSVivien Didelot 			ds->ops->fixed_link_update(ds, p->port, status);
1067b71be352SAndrew Lunn 	}
1068ce31b31cSFlorian Fainelli 
1069ce31b31cSFlorian Fainelli 	return 0;
1070ce31b31cSFlorian Fainelli }
1071ce31b31cSFlorian Fainelli 
107291da11f8SLennert Buytenhek /* slave device setup *******************************************************/
1073c305c165SFlorian Fainelli static int dsa_slave_phy_connect(struct dsa_slave_priv *p,
1074cd28a1a9SFlorian Fainelli 				 struct net_device *slave_dev,
1075cd28a1a9SFlorian Fainelli 				 int addr)
1076c305c165SFlorian Fainelli {
1077c305c165SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
1078c305c165SFlorian Fainelli 
10797f854420SAndrew Lunn 	p->phy = mdiobus_get_phy(ds->slave_mii_bus, addr);
1080d25b8e74SRussell King 	if (!p->phy) {
1081d25b8e74SRussell King 		netdev_err(slave_dev, "no phy at %d\n", addr);
1082c305c165SFlorian Fainelli 		return -ENODEV;
1083d25b8e74SRussell King 	}
1084c305c165SFlorian Fainelli 
1085c305c165SFlorian Fainelli 	/* Use already configured phy mode */
1086211c504aSFlorian Fainelli 	if (p->phy_interface == PHY_INTERFACE_MODE_NA)
1087c305c165SFlorian Fainelli 		p->phy_interface = p->phy->interface;
1088c305c165SFlorian Fainelli 	phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
1089c305c165SFlorian Fainelli 			   p->phy_interface);
1090c305c165SFlorian Fainelli 
1091c305c165SFlorian Fainelli 	return 0;
1092c305c165SFlorian Fainelli }
1093c305c165SFlorian Fainelli 
10949697f1cdSFlorian Fainelli static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
10950d8bcdd3SFlorian Fainelli 				struct net_device *slave_dev)
10960d8bcdd3SFlorian Fainelli {
10970d8bcdd3SFlorian Fainelli 	struct dsa_switch *ds = p->parent;
10980d8bcdd3SFlorian Fainelli 	struct device_node *phy_dn, *port_dn;
1099ce31b31cSFlorian Fainelli 	bool phy_is_fixed = false;
11006819563eSFlorian Fainelli 	u32 phy_flags = 0;
110119334920SGuenter Roeck 	int mode, ret;
11020d8bcdd3SFlorian Fainelli 
1103189b0d93SAndrew Lunn 	port_dn = ds->ports[p->port].dn;
110419334920SGuenter Roeck 	mode = of_get_phy_mode(port_dn);
110519334920SGuenter Roeck 	if (mode < 0)
110619334920SGuenter Roeck 		mode = PHY_INTERFACE_MODE_NA;
110719334920SGuenter Roeck 	p->phy_interface = mode;
11080d8bcdd3SFlorian Fainelli 
11090d8bcdd3SFlorian Fainelli 	phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
11100d8bcdd3SFlorian Fainelli 	if (of_phy_is_fixed_link(port_dn)) {
11110d8bcdd3SFlorian Fainelli 		/* In the case of a fixed PHY, the DT node associated
11120d8bcdd3SFlorian Fainelli 		 * to the fixed PHY is the Port DT node
11130d8bcdd3SFlorian Fainelli 		 */
11140d8bcdd3SFlorian Fainelli 		ret = of_phy_register_fixed_link(port_dn);
11150d8bcdd3SFlorian Fainelli 		if (ret) {
1116d25b8e74SRussell King 			netdev_err(slave_dev, "failed to register fixed PHY: %d\n", ret);
11179697f1cdSFlorian Fainelli 			return ret;
11180d8bcdd3SFlorian Fainelli 		}
1119ce31b31cSFlorian Fainelli 		phy_is_fixed = true;
11200d8bcdd3SFlorian Fainelli 		phy_dn = port_dn;
11210d8bcdd3SFlorian Fainelli 	}
11220d8bcdd3SFlorian Fainelli 
11239d490b4eSVivien Didelot 	if (ds->ops->get_phy_flags)
11249d490b4eSVivien Didelot 		phy_flags = ds->ops->get_phy_flags(ds, p->port);
11256819563eSFlorian Fainelli 
1126cd28a1a9SFlorian Fainelli 	if (phy_dn) {
1127d25b8e74SRussell King 		int phy_id = of_mdio_parse_addr(&slave_dev->dev, phy_dn);
1128d25b8e74SRussell King 
1129cd28a1a9SFlorian Fainelli 		/* If this PHY address is part of phys_mii_mask, which means
1130cd28a1a9SFlorian Fainelli 		 * that we need to divert reads and writes to/from it, then we
1131cd28a1a9SFlorian Fainelli 		 * want to bind this device using the slave MII bus created by
1132cd28a1a9SFlorian Fainelli 		 * DSA to make that happen.
1133cd28a1a9SFlorian Fainelli 		 */
1134d25b8e74SRussell King 		if (!phy_is_fixed && phy_id >= 0 &&
1135d25b8e74SRussell King 		    (ds->phys_mii_mask & (1 << phy_id))) {
1136d25b8e74SRussell King 			ret = dsa_slave_phy_connect(p, slave_dev, phy_id);
1137d25b8e74SRussell King 			if (ret) {
1138d25b8e74SRussell King 				netdev_err(slave_dev, "failed to connect to phy%d: %d\n", phy_id, ret);
1139cd28a1a9SFlorian Fainelli 				return ret;
1140d25b8e74SRussell King 			}
1141cd28a1a9SFlorian Fainelli 		} else {
11420d8bcdd3SFlorian Fainelli 			p->phy = of_phy_connect(slave_dev, phy_dn,
1143cd28a1a9SFlorian Fainelli 						dsa_slave_adjust_link,
1144cd28a1a9SFlorian Fainelli 						phy_flags,
11450d8bcdd3SFlorian Fainelli 						p->phy_interface);
1146cd28a1a9SFlorian Fainelli 		}
1147cd28a1a9SFlorian Fainelli 	}
11480d8bcdd3SFlorian Fainelli 
1149ce31b31cSFlorian Fainelli 	if (p->phy && phy_is_fixed)
1150ce31b31cSFlorian Fainelli 		fixed_phy_set_link_update(p->phy, dsa_slave_fixed_link_update);
1151ce31b31cSFlorian Fainelli 
11520d8bcdd3SFlorian Fainelli 	/* We could not connect to a designated PHY, so use the switch internal
11530d8bcdd3SFlorian Fainelli 	 * MDIO bus instead
11540d8bcdd3SFlorian Fainelli 	 */
1155b31f65fbSAndrew Lunn 	if (!p->phy) {
1156cd28a1a9SFlorian Fainelli 		ret = dsa_slave_phy_connect(p, slave_dev, p->port);
1157d25b8e74SRussell King 		if (ret) {
1158d25b8e74SRussell King 			netdev_err(slave_dev, "failed to connect to port %d: %d\n", p->port, ret);
1159c305c165SFlorian Fainelli 			return ret;
1160d25b8e74SRussell King 		}
11610d8bcdd3SFlorian Fainelli 	}
11629697f1cdSFlorian Fainelli 
11632220943aSAndrew Lunn 	phy_attached_info(p->phy);
11642220943aSAndrew Lunn 
11659697f1cdSFlorian Fainelli 	return 0;
1166b31f65fbSAndrew Lunn }
11670d8bcdd3SFlorian Fainelli 
1168448b4482SAndrew Lunn static struct lock_class_key dsa_slave_netdev_xmit_lock_key;
1169448b4482SAndrew Lunn static void dsa_slave_set_lockdep_class_one(struct net_device *dev,
1170448b4482SAndrew Lunn 					    struct netdev_queue *txq,
1171448b4482SAndrew Lunn 					    void *_unused)
1172448b4482SAndrew Lunn {
1173448b4482SAndrew Lunn 	lockdep_set_class(&txq->_xmit_lock,
1174448b4482SAndrew Lunn 			  &dsa_slave_netdev_xmit_lock_key);
1175448b4482SAndrew Lunn }
1176448b4482SAndrew Lunn 
117724462549SFlorian Fainelli int dsa_slave_suspend(struct net_device *slave_dev)
117824462549SFlorian Fainelli {
117924462549SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(slave_dev);
118024462549SFlorian Fainelli 
118124462549SFlorian Fainelli 	if (p->phy) {
118224462549SFlorian Fainelli 		phy_stop(p->phy);
118324462549SFlorian Fainelli 		p->old_pause = -1;
118424462549SFlorian Fainelli 		p->old_link = -1;
118524462549SFlorian Fainelli 		p->old_duplex = -1;
118624462549SFlorian Fainelli 		phy_suspend(p->phy);
118724462549SFlorian Fainelli 	}
118824462549SFlorian Fainelli 
118924462549SFlorian Fainelli 	return 0;
119024462549SFlorian Fainelli }
119124462549SFlorian Fainelli 
119224462549SFlorian Fainelli int dsa_slave_resume(struct net_device *slave_dev)
119324462549SFlorian Fainelli {
119424462549SFlorian Fainelli 	struct dsa_slave_priv *p = netdev_priv(slave_dev);
119524462549SFlorian Fainelli 
119624462549SFlorian Fainelli 	netif_device_attach(slave_dev);
119724462549SFlorian Fainelli 
119824462549SFlorian Fainelli 	if (p->phy) {
119924462549SFlorian Fainelli 		phy_resume(p->phy);
120024462549SFlorian Fainelli 		phy_start(p->phy);
120124462549SFlorian Fainelli 	}
120224462549SFlorian Fainelli 
120324462549SFlorian Fainelli 	return 0;
120424462549SFlorian Fainelli }
120524462549SFlorian Fainelli 
1206d87d6f44SGuenter Roeck int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
120783c0afaeSAndrew Lunn 		     int port, const char *name)
120891da11f8SLennert Buytenhek {
1209badf3adaSFlorian Fainelli 	struct dsa_switch_tree *dst = ds->dst;
121083c0afaeSAndrew Lunn 	struct net_device *master;
121191da11f8SLennert Buytenhek 	struct net_device *slave_dev;
121291da11f8SLennert Buytenhek 	struct dsa_slave_priv *p;
121391da11f8SLennert Buytenhek 	int ret;
121491da11f8SLennert Buytenhek 
121583c0afaeSAndrew Lunn 	master = ds->dst->master_netdev;
121683c0afaeSAndrew Lunn 	if (ds->master_netdev)
121783c0afaeSAndrew Lunn 		master = ds->master_netdev;
121883c0afaeSAndrew Lunn 
1219c835a677STom Gundersen 	slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name,
1220c835a677STom Gundersen 				 NET_NAME_UNKNOWN, ether_setup);
122191da11f8SLennert Buytenhek 	if (slave_dev == NULL)
1222d87d6f44SGuenter Roeck 		return -ENOMEM;
122391da11f8SLennert Buytenhek 
122491da11f8SLennert Buytenhek 	slave_dev->features = master->vlan_features;
12257ad24ea4SWilfried Klaebe 	slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
12262fcc8005SBjørn Mork 	eth_hw_addr_inherit(slave_dev, master);
12270a5f107bSPhil Sutter 	slave_dev->priv_flags |= IFF_NO_QUEUE;
12283e8a72d1SFlorian Fainelli 	slave_dev->netdev_ops = &dsa_slave_netdev_ops;
12299d47c0a2SJiri Pirko 	slave_dev->switchdev_ops = &dsa_slave_switchdev_ops;
1230f37db85dSFlorian Fainelli 	SET_NETDEV_DEVTYPE(slave_dev, &dsa_type);
1231d442ad4aSStephen Hemminger 
1232448b4482SAndrew Lunn 	netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one,
1233448b4482SAndrew Lunn 				 NULL);
1234448b4482SAndrew Lunn 
123591da11f8SLennert Buytenhek 	SET_NETDEV_DEV(slave_dev, parent);
1236189b0d93SAndrew Lunn 	slave_dev->dev.of_node = ds->ports[port].dn;
123791da11f8SLennert Buytenhek 	slave_dev->vlan_features = master->vlan_features;
123891da11f8SLennert Buytenhek 
123991da11f8SLennert Buytenhek 	p = netdev_priv(slave_dev);
124091da11f8SLennert Buytenhek 	p->parent = ds;
124191da11f8SLennert Buytenhek 	p->port = port;
124239a7f2a4SAndrew Lunn 	p->xmit = dst->tag_ops->xmit;
12435075314eSAlexander Duyck 
12440d8bcdd3SFlorian Fainelli 	p->old_pause = -1;
12450d8bcdd3SFlorian Fainelli 	p->old_link = -1;
12460d8bcdd3SFlorian Fainelli 	p->old_duplex = -1;
12470d8bcdd3SFlorian Fainelli 
1248c8b09808SAndrew Lunn 	ds->ports[port].netdev = slave_dev;
124991da11f8SLennert Buytenhek 	ret = register_netdev(slave_dev);
125091da11f8SLennert Buytenhek 	if (ret) {
1251a2ae6007SJoe Perches 		netdev_err(master, "error %d registering interface %s\n",
1252a2ae6007SJoe Perches 			   ret, slave_dev->name);
1253c8b09808SAndrew Lunn 		ds->ports[port].netdev = NULL;
125491da11f8SLennert Buytenhek 		free_netdev(slave_dev);
1255d87d6f44SGuenter Roeck 		return ret;
125691da11f8SLennert Buytenhek 	}
125791da11f8SLennert Buytenhek 
125891da11f8SLennert Buytenhek 	netif_carrier_off(slave_dev);
125991da11f8SLennert Buytenhek 
12600071f56eSAndrew Lunn 	ret = dsa_slave_phy_setup(p, slave_dev);
12610071f56eSAndrew Lunn 	if (ret) {
12620071f56eSAndrew Lunn 		netdev_err(master, "error %d setting up slave phy\n", ret);
126373dcb556SFlorian Fainelli 		unregister_netdev(slave_dev);
12640071f56eSAndrew Lunn 		free_netdev(slave_dev);
12650071f56eSAndrew Lunn 		return ret;
12660071f56eSAndrew Lunn 	}
12670071f56eSAndrew Lunn 
1268d87d6f44SGuenter Roeck 	return 0;
126991da11f8SLennert Buytenhek }
1270b73adef6SFlorian Fainelli 
1271cda5c15bSNeil Armstrong void dsa_slave_destroy(struct net_device *slave_dev)
1272cda5c15bSNeil Armstrong {
1273cda5c15bSNeil Armstrong 	struct dsa_slave_priv *p = netdev_priv(slave_dev);
1274cda5c15bSNeil Armstrong 
1275cda5c15bSNeil Armstrong 	netif_carrier_off(slave_dev);
1276cda5c15bSNeil Armstrong 	if (p->phy)
1277cda5c15bSNeil Armstrong 		phy_disconnect(p->phy);
1278cda5c15bSNeil Armstrong 	unregister_netdev(slave_dev);
1279cda5c15bSNeil Armstrong 	free_netdev(slave_dev);
1280cda5c15bSNeil Armstrong }
1281cda5c15bSNeil Armstrong 
1282b73adef6SFlorian Fainelli static bool dsa_slave_dev_check(struct net_device *dev)
1283b73adef6SFlorian Fainelli {
1284b73adef6SFlorian Fainelli 	return dev->netdev_ops == &dsa_slave_netdev_ops;
1285b73adef6SFlorian Fainelli }
1286b73adef6SFlorian Fainelli 
12876debb68aSVivien Didelot static int dsa_slave_port_upper_event(struct net_device *dev,
12886debb68aSVivien Didelot 				      unsigned long event, void *ptr)
1289b73adef6SFlorian Fainelli {
12906debb68aSVivien Didelot 	struct netdev_notifier_changeupper_info *info = ptr;
12916debb68aSVivien Didelot 	struct net_device *upper = info->upper_dev;
1292b73adef6SFlorian Fainelli 	int err = 0;
1293b73adef6SFlorian Fainelli 
12946debb68aSVivien Didelot 	switch (event) {
12956debb68aSVivien Didelot 	case NETDEV_CHANGEUPPER:
12966debb68aSVivien Didelot 		if (netif_is_bridge_master(upper)) {
12976debb68aSVivien Didelot 			if (info->linking)
12986debb68aSVivien Didelot 				err = dsa_slave_bridge_port_join(dev, upper);
12996debb68aSVivien Didelot 			else
130016bfa702SVivien Didelot 				dsa_slave_bridge_port_leave(dev);
13016debb68aSVivien Didelot 		}
1302b73adef6SFlorian Fainelli 
13036debb68aSVivien Didelot 		break;
13046debb68aSVivien Didelot 	}
13056debb68aSVivien Didelot 
13066debb68aSVivien Didelot 	return notifier_from_errno(err);
13076debb68aSVivien Didelot }
13086debb68aSVivien Didelot 
13096debb68aSVivien Didelot static int dsa_slave_port_event(struct net_device *dev, unsigned long event,
13106debb68aSVivien Didelot 				void *ptr)
13116debb68aSVivien Didelot {
13126debb68aSVivien Didelot 	switch (event) {
13136debb68aSVivien Didelot 	case NETDEV_CHANGEUPPER:
13146debb68aSVivien Didelot 		return dsa_slave_port_upper_event(dev, event, ptr);
13156debb68aSVivien Didelot 	}
13166debb68aSVivien Didelot 
13176debb68aSVivien Didelot 	return NOTIFY_DONE;
1318b73adef6SFlorian Fainelli }
1319b73adef6SFlorian Fainelli 
1320b73adef6SFlorian Fainelli int dsa_slave_netdevice_event(struct notifier_block *unused,
1321b73adef6SFlorian Fainelli 			      unsigned long event, void *ptr)
1322b73adef6SFlorian Fainelli {
13236debb68aSVivien Didelot 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1324b73adef6SFlorian Fainelli 
13256debb68aSVivien Didelot 	if (dsa_slave_dev_check(dev))
13266debb68aSVivien Didelot 		return dsa_slave_port_event(dev, event, ptr);
1327b73adef6SFlorian Fainelli 
1328b73adef6SFlorian Fainelli 	return NOTIFY_DONE;
1329b73adef6SFlorian Fainelli }
1330