Blame view

net/dsa/dsa.c 6.9 KB
91da11f87   Lennert Buytenhek   net: Distributed ...
1
2
  /*
   * net/dsa/dsa.c - Hardware switch handling
e84665c9c   Lennert Buytenhek   dsa: add switch c...
3
   * Copyright (c) 2008-2009 Marvell Semiconductor
5e95329b7   Florian Fainelli   dsa: add device t...
4
   * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
91da11f87   Lennert Buytenhek   net: Distributed ...
5
6
7
8
9
10
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   */
51579c3f1   Guenter Roeck   net: dsa: Add sup...
11
  #include <linux/device.h>
91da11f87   Lennert Buytenhek   net: Distributed ...
12
  #include <linux/list.h>
91da11f87   Lennert Buytenhek   net: Distributed ...
13
  #include <linux/platform_device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/slab.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
15
  #include <linux/module.h>
5e95329b7   Florian Fainelli   dsa: add device t...
16
17
18
  #include <linux/of.h>
  #include <linux/of_mdio.h>
  #include <linux/of_platform.h>
769a02028   Florian Fainelli   net: dsa: utilize...
19
  #include <linux/of_net.h>
cc30c1634   Andrew Lunn   net: dsa: Add sup...
20
  #include <linux/of_gpio.h>
c6e970a04   Andrew Lunn   net: break includ...
21
  #include <linux/netdevice.h>
51579c3f1   Guenter Roeck   net: dsa: Add sup...
22
  #include <linux/sysfs.h>
cbc5d90b3   Neil Armstrong   net: dsa: complet...
23
  #include <linux/phy_fixed.h>
85beabfec   Arnd Bergmann   net: dsa: include...
24
  #include <linux/gpio/consumer.h>
a86d8becc   Florian Fainelli   net: dsa: Factor ...
25
  #include <linux/etherdevice.h>
ea5dd34be   Vivien Didelot   net: dsa: include...
26

91da11f87   Lennert Buytenhek   net: Distributed ...
27
  #include "dsa_priv.h"
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
28
29
30
31
32
33
34
35
36
37
38
39
40
  static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb,
  					    struct net_device *dev)
  {
  	/* Just return the original SKB */
  	return skb;
  }
  
  static const struct dsa_device_ops none_ops = {
  	.xmit	= dsa_slave_notag_xmit,
  	.rcv	= NULL,
  };
  
  const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = {
eb7b72112   Andrew Lunn   net: dsa: Sort DS...
41
42
43
  #ifdef CONFIG_NET_DSA_TAG_BRCM
  	[DSA_TAG_PROTO_BRCM] = &brcm_netdev_ops,
  #endif
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
44
45
46
47
48
49
  #ifdef CONFIG_NET_DSA_TAG_DSA
  	[DSA_TAG_PROTO_DSA] = &dsa_netdev_ops,
  #endif
  #ifdef CONFIG_NET_DSA_TAG_EDSA
  	[DSA_TAG_PROTO_EDSA] = &edsa_netdev_ops,
  #endif
8b8010fb7   Woojung Huh   dsa: add support ...
50
51
52
  #ifdef CONFIG_NET_DSA_TAG_KSZ
  	[DSA_TAG_PROTO_KSZ] = &ksz_netdev_ops,
  #endif
eb7b72112   Andrew Lunn   net: dsa: Sort DS...
53
54
  #ifdef CONFIG_NET_DSA_TAG_LAN9303
  	[DSA_TAG_PROTO_LAN9303] = &lan9303_netdev_ops,
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
55
  #endif
eb7b72112   Andrew Lunn   net: dsa: Sort DS...
56
57
  #ifdef CONFIG_NET_DSA_TAG_MTK
  	[DSA_TAG_PROTO_MTK] = &mtk_netdev_ops,
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
58
  #endif
cafdc45c9   John Crispin   net-next: dsa: ad...
59
60
61
  #ifdef CONFIG_NET_DSA_TAG_QCA
  	[DSA_TAG_PROTO_QCA] = &qca_netdev_ops,
  #endif
eb7b72112   Andrew Lunn   net: dsa: Sort DS...
62
63
  #ifdef CONFIG_NET_DSA_TAG_TRAILER
  	[DSA_TAG_PROTO_TRAILER] = &trailer_netdev_ops,
e8fe177a6   Juergen Beisert   net: dsa: add sup...
64
  #endif
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
65
66
  	[DSA_TAG_PROTO_NONE] = &none_ops,
  };
91da11f87   Lennert Buytenhek   net: Distributed ...
67

47d0dcc35   Vivien Didelot   net: dsa: remove ...
68
  int dsa_cpu_dsa_setup(struct dsa_port *port)
39b0c7051   Andrew Lunn   net: dsa: Allow c...
69
  {
47d0dcc35   Vivien Didelot   net: dsa: remove ...
70
71
  	struct device_node *port_dn = port->dn;
  	struct dsa_switch *ds = port->ds;
39b0c7051   Andrew Lunn   net: dsa: Allow c...
72
  	struct phy_device *phydev;
9b8e895c4   Andrew Lunn   net: dsa: Split u...
73
74
75
76
77
  	int ret, mode;
  
  	if (of_phy_is_fixed_link(port_dn)) {
  		ret = of_phy_register_fixed_link(port_dn);
  		if (ret) {
47d0dcc35   Vivien Didelot   net: dsa: remove ...
78
79
  			dev_err(ds->dev, "failed to register fixed PHY
  ");
9b8e895c4   Andrew Lunn   net: dsa: Split u...
80
81
82
83
84
85
86
87
88
89
90
  			return ret;
  		}
  		phydev = of_phy_find_device(port_dn);
  
  		mode = of_get_phy_mode(port_dn);
  		if (mode < 0)
  			mode = PHY_INTERFACE_MODE_NA;
  		phydev->interface = mode;
  
  		genphy_config_init(phydev);
  		genphy_read_status(phydev);
9d490b4ee   Vivien Didelot   net: dsa: rename ...
91
  		if (ds->ops->adjust_link)
47d0dcc35   Vivien Didelot   net: dsa: remove ...
92
  			ds->ops->adjust_link(ds, port->index, phydev);
fd05d7b18   Johan Hovold   net: dsa: fix fix...
93
94
  
  		put_device(&phydev->mdio.dev);
9b8e895c4   Andrew Lunn   net: dsa: Split u...
95
96
97
98
  	}
  
  	return 0;
  }
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
99
100
101
102
103
104
105
106
107
108
109
110
111
  const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol)
  {
  	const struct dsa_device_ops *ops;
  
  	if (tag_protocol >= DSA_TAG_LAST)
  		return ERR_PTR(-EINVAL);
  	ops = dsa_device_ops[tag_protocol];
  
  	if (!ops)
  		return ERR_PTR(-ENOPROTOOPT);
  
  	return ops;
  }
937c7df85   Florian Fainelli   net: dsa: Pass ds...
112
  int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp)
0c73c523c   Florian Fainelli   net: dsa: Initial...
113
  {
937c7df85   Florian Fainelli   net: dsa: Pass ds...
114
  	struct dsa_switch *ds = cpu_dp->ds;
0c73c523c   Florian Fainelli   net: dsa: Initial...
115
116
  	struct net_device *master;
  	struct ethtool_ops *cpu_ops;
67dbb9d43   Florian Fainelli   net: dsa: Relocat...
117
  	master = cpu_dp->netdev;
0c73c523c   Florian Fainelli   net: dsa: Initial...
118
119
120
  	cpu_ops = devm_kzalloc(ds->dev, sizeof(*cpu_ops), GFP_KERNEL);
  	if (!cpu_ops)
  		return -ENOMEM;
67dbb9d43   Florian Fainelli   net: dsa: Relocat...
121
  	memcpy(&cpu_dp->ethtool_ops, master->ethtool_ops,
0c73c523c   Florian Fainelli   net: dsa: Initial...
122
  	       sizeof(struct ethtool_ops));
67dbb9d43   Florian Fainelli   net: dsa: Relocat...
123
124
  	cpu_dp->orig_ethtool_ops = master->ethtool_ops;
  	memcpy(cpu_ops, &cpu_dp->ethtool_ops,
0c73c523c   Florian Fainelli   net: dsa: Initial...
125
126
127
128
129
130
  	       sizeof(struct ethtool_ops));
  	dsa_cpu_port_ethtool_init(cpu_ops);
  	master->ethtool_ops = cpu_ops;
  
  	return 0;
  }
937c7df85   Florian Fainelli   net: dsa: Pass ds...
131
  void dsa_cpu_port_ethtool_restore(struct dsa_port *cpu_dp)
0c73c523c   Florian Fainelli   net: dsa: Initial...
132
  {
67dbb9d43   Florian Fainelli   net: dsa: Relocat...
133
  	cpu_dp->netdev->ethtool_ops = cpu_dp->orig_ethtool_ops;
0c73c523c   Florian Fainelli   net: dsa: Initial...
134
  }
293784a8f   Florian Fainelli   net: dsa: Make mo...
135
  void dsa_cpu_dsa_destroy(struct dsa_port *port)
91da11f87   Lennert Buytenhek   net: Distributed ...
136
  {
293784a8f   Florian Fainelli   net: dsa: Make mo...
137
  	struct device_node *port_dn = port->dn;
3f65047c8   Johan Hovold   of_mdio: add help...
138
139
  	if (of_phy_is_fixed_link(port_dn))
  		of_phy_deregister_fixed_link(port_dn);
9b8e895c4   Andrew Lunn   net: dsa: Split u...
140
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
  static int dev_is_class(struct device *dev, void *class)
  {
  	if (dev->class != NULL && !strcmp(dev->class->name, class))
  		return 1;
  
  	return 0;
  }
  
  static struct device *dev_find_class(struct device *parent, char *class)
  {
  	if (dev_is_class(parent, class)) {
  		get_device(parent);
  		return parent;
  	}
  
  	return device_find_child(parent, class, dev_is_class);
  }
14b89f36e   Florian Fainelli   net: dsa: Rename ...
158
  struct net_device *dsa_dev_to_net_device(struct device *dev)
91da11f87   Lennert Buytenhek   net: Distributed ...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  {
  	struct device *d;
  
  	d = dev_find_class(dev, "net");
  	if (d != NULL) {
  		struct net_device *nd;
  
  		nd = to_net_dev(d);
  		dev_hold(nd);
  		put_device(d);
  
  		return nd;
  	}
  
  	return NULL;
  }
14b89f36e   Florian Fainelli   net: dsa: Rename ...
175
  EXPORT_SYMBOL_GPL(dsa_dev_to_net_device);
91da11f87   Lennert Buytenhek   net: Distributed ...
176

3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
177
  static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
89e49506b   Florian Westphal   dsa: remove unuse...
178
  			  struct packet_type *pt, struct net_device *unused)
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
179
180
  {
  	struct dsa_switch_tree *dst = dev->dsa_ptr;
a86d8becc   Florian Fainelli   net: dsa: Factor ...
181
  	struct sk_buff *nskb = NULL;
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
182
  	struct pcpu_sw_netstats *s;
f613ed665   Florian Fainelli   net: dsa: Add sup...
183
  	struct dsa_slave_priv *p;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
184
185
186
187
188
  
  	if (unlikely(dst == NULL)) {
  		kfree_skb(skb);
  		return 0;
  	}
16c5dcb13   Florian Fainelli   net: dsa: Move sk...
189
190
191
  	skb = skb_unshare(skb, GFP_ATOMIC);
  	if (!skb)
  		return 0;
89e49506b   Florian Westphal   dsa: remove unuse...
192
  	nskb = dst->rcv(skb, dev, pt);
a86d8becc   Florian Fainelli   net: dsa: Factor ...
193
194
195
196
197
198
  	if (!nskb) {
  		kfree_skb(skb);
  		return 0;
  	}
  
  	skb = nskb;
f613ed665   Florian Fainelli   net: dsa: Add sup...
199
  	p = netdev_priv(skb->dev);
a86d8becc   Florian Fainelli   net: dsa: Factor ...
200
201
202
  	skb_push(skb, ETH_HLEN);
  	skb->pkt_type = PACKET_HOST;
  	skb->protocol = eth_type_trans(skb, skb->dev);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
203
204
205
206
207
  	s = this_cpu_ptr(p->stats64);
  	u64_stats_update_begin(&s->syncp);
  	s->rx_packets++;
  	s->rx_bytes += skb->len;
  	u64_stats_update_end(&s->syncp);
a86d8becc   Florian Fainelli   net: dsa: Factor ...
208
209
210
211
  
  	netif_receive_skb(skb);
  
  	return 0;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
212
  }
ac2629a47   Florian Fainelli   net: dsa: Move ds...
213
  #ifdef CONFIG_PM_SLEEP
e7d53ad32   Vivien Didelot   net: dsa: unexpor...
214
215
216
217
  static bool dsa_is_port_initialized(struct dsa_switch *ds, int p)
  {
  	return ds->enabled_port_mask & (1 << p) && ds->ports[p].netdev;
  }
ac2629a47   Florian Fainelli   net: dsa: Move ds...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  int dsa_switch_suspend(struct dsa_switch *ds)
  {
  	int i, ret = 0;
  
  	/* Suspend slave network devices */
  	for (i = 0; i < ds->num_ports; i++) {
  		if (!dsa_is_port_initialized(ds, i))
  			continue;
  
  		ret = dsa_slave_suspend(ds->ports[i].netdev);
  		if (ret)
  			return ret;
  	}
  
  	if (ds->ops->suspend)
  		ret = ds->ops->suspend(ds);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dsa_switch_suspend);
  
  int dsa_switch_resume(struct dsa_switch *ds)
  {
  	int i, ret = 0;
  
  	if (ds->ops->resume)
  		ret = ds->ops->resume(ds);
  
  	if (ret)
  		return ret;
  
  	/* Resume slave network devices */
  	for (i = 0; i < ds->num_ports; i++) {
  		if (!dsa_is_port_initialized(ds, i))
  			continue;
  
  		ret = dsa_slave_resume(ds->ports[i].netdev);
  		if (ret)
  			return ret;
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(dsa_switch_resume);
  #endif
61b7363ff   Florian Fainelli   net: dsa: make ds...
263
  static struct packet_type dsa_pack_type __read_mostly = {
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
264
265
266
  	.type	= cpu_to_be16(ETH_P_XDSA),
  	.func	= dsa_switch_rcv,
  };
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
267
268
269
270
271
272
  static struct workqueue_struct *dsa_owq;
  
  bool dsa_schedule_work(struct work_struct *work)
  {
  	return queue_work(dsa_owq, work);
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
273
274
  static int __init dsa_init_module(void)
  {
7df899c36   Ben Hutchings   dsa: Combine core...
275
  	int rc;
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
276
277
278
279
  	dsa_owq = alloc_ordered_workqueue("dsa_ordered",
  					  WQ_MEM_RECLAIM);
  	if (!dsa_owq)
  		return -ENOMEM;
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
280
281
282
  	rc = dsa_slave_register_notifier();
  	if (rc)
  		return rc;
b73adef67   Florian Fainelli   net: dsa: integra...
283

a6a71f19f   Vivien Didelot   net: dsa: isolate...
284
  	rc = dsa_legacy_register();
7df899c36   Ben Hutchings   dsa: Combine core...
285
286
  	if (rc)
  		return rc;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
287
  	dev_add_pack(&dsa_pack_type);
7df899c36   Ben Hutchings   dsa: Combine core...
288
  	return 0;
91da11f87   Lennert Buytenhek   net: Distributed ...
289
290
291
292
293
  }
  module_init(dsa_init_module);
  
  static void __exit dsa_cleanup_module(void)
  {
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
294
  	dsa_slave_unregister_notifier();
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
295
  	dev_remove_pack(&dsa_pack_type);
a6a71f19f   Vivien Didelot   net: dsa: isolate...
296
  	dsa_legacy_unregister();
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
297
  	destroy_workqueue(dsa_owq);
91da11f87   Lennert Buytenhek   net: Distributed ...
298
299
  }
  module_exit(dsa_cleanup_module);
577d6a7c3   Rusty Russell   module: fix missi...
300
  MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
91da11f87   Lennert Buytenhek   net: Distributed ...
301
302
303
  MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:dsa");