Blame view

net/dsa/dsa.c 11.5 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
91da11f87   Lennert Buytenhek   net: Distributed ...
2
3
  /*
   * net/dsa/dsa.c - Hardware switch handling
e84665c9c   Lennert Buytenhek   dsa: add switch c...
4
   * Copyright (c) 2008-2009 Marvell Semiconductor
5e95329b7   Florian Fainelli   dsa: add device t...
5
   * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
91da11f87   Lennert Buytenhek   net: Distributed ...
6
   */
51579c3f1   Guenter Roeck   net: dsa: Add sup...
7
  #include <linux/device.h>
91da11f87   Lennert Buytenhek   net: Distributed ...
8
  #include <linux/list.h>
91da11f87   Lennert Buytenhek   net: Distributed ...
9
  #include <linux/platform_device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
10
  #include <linux/slab.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
11
  #include <linux/module.h>
60724d4ba   Florian Fainelli   net: dsa: Add sup...
12
  #include <linux/notifier.h>
5e95329b7   Florian Fainelli   dsa: add device t...
13
14
15
  #include <linux/of.h>
  #include <linux/of_mdio.h>
  #include <linux/of_platform.h>
769a02028   Florian Fainelli   net: dsa: utilize...
16
  #include <linux/of_net.h>
c6e970a04   Andrew Lunn   net: break includ...
17
  #include <linux/netdevice.h>
51579c3f1   Guenter Roeck   net: dsa: Add sup...
18
  #include <linux/sysfs.h>
cbc5d90b3   Neil Armstrong   net: dsa: complet...
19
  #include <linux/phy_fixed.h>
90af1059c   Brandon Streiff   net: dsa: forward...
20
  #include <linux/ptp_classify.h>
a86d8becc   Florian Fainelli   net: dsa: Factor ...
21
  #include <linux/etherdevice.h>
ea5dd34be   Vivien Didelot   net: dsa: include...
22

91da11f87   Lennert Buytenhek   net: Distributed ...
23
  #include "dsa_priv.h"
bdc6fe5bb   Andrew Lunn   dsa: Keep link li...
24
25
  static LIST_HEAD(dsa_tag_drivers_list);
  static DEFINE_MUTEX(dsa_tag_drivers_lock);
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
26
27
28
29
30
31
32
33
  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 = {
875138f81   Andrew Lunn   dsa: Move tagger ...
34
  	.name	= "none",
056eed2fb   Andrew Lunn   dsa: Add TAG prot...
35
  	.proto	= DSA_TAG_PROTO_NONE,
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
36
37
38
  	.xmit	= dsa_slave_notag_xmit,
  	.rcv	= NULL,
  };
409065b06   Andrew Lunn   dsa: Register the...
39
  DSA_TAG_DRIVER(none_ops);
bdc6fe5bb   Andrew Lunn   dsa: Keep link li...
40
41
42
43
44
45
46
47
48
  static void dsa_tag_driver_register(struct dsa_tag_driver *dsa_tag_driver,
  				    struct module *owner)
  {
  	dsa_tag_driver->owner = owner;
  
  	mutex_lock(&dsa_tag_drivers_lock);
  	list_add_tail(&dsa_tag_driver->list, &dsa_tag_drivers_list);
  	mutex_unlock(&dsa_tag_drivers_lock);
  }
d3b8c0498   Andrew Lunn   dsa: Add boilerpl...
49
50
51
  void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[],
  			      unsigned int count, struct module *owner)
  {
bdc6fe5bb   Andrew Lunn   dsa: Keep link li...
52
53
54
55
56
57
58
59
60
61
62
  	unsigned int i;
  
  	for (i = 0; i < count; i++)
  		dsa_tag_driver_register(dsa_tag_driver_array[i], owner);
  }
  
  static void dsa_tag_driver_unregister(struct dsa_tag_driver *dsa_tag_driver)
  {
  	mutex_lock(&dsa_tag_drivers_lock);
  	list_del(&dsa_tag_driver->list);
  	mutex_unlock(&dsa_tag_drivers_lock);
d3b8c0498   Andrew Lunn   dsa: Add boilerpl...
63
64
65
66
67
68
  }
  EXPORT_SYMBOL_GPL(dsa_tag_drivers_register);
  
  void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[],
  				unsigned int count)
  {
bdc6fe5bb   Andrew Lunn   dsa: Keep link li...
69
70
71
72
  	unsigned int i;
  
  	for (i = 0; i < count; i++)
  		dsa_tag_driver_unregister(dsa_tag_driver_array[i]);
d3b8c0498   Andrew Lunn   dsa: Add boilerpl...
73
74
  }
  EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister);
98cdb4807   Florian Fainelli   net: dsa: Expose ...
75
76
  const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops)
  {
875138f81   Andrew Lunn   dsa: Move tagger ...
77
  	return ops->name;
98cdb4807   Florian Fainelli   net: dsa: Expose ...
78
  };
c39e2a1d7   Andrew Lunn   dsa: Rename dsa_r...
79
  const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol)
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
80
  {
367561753   Andrew Lunn   dsa: Make use of ...
81
  	struct dsa_tag_driver *dsa_tag_driver;
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
82
  	const struct dsa_device_ops *ops;
367561753   Andrew Lunn   dsa: Make use of ...
83
  	bool found = false;
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
84

ee91a83e0   Christophe JAILLET   net: dsa: Simplif...
85
  	request_module("%s%d", DSA_TAG_DRIVER_ALIAS, tag_protocol);
367561753   Andrew Lunn   dsa: Make use of ...
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
  
  	mutex_lock(&dsa_tag_drivers_lock);
  	list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) {
  		ops = dsa_tag_driver->ops;
  		if (ops->proto == tag_protocol) {
  			found = true;
  			break;
  		}
  	}
  
  	if (found) {
  		if (!try_module_get(dsa_tag_driver->owner))
  			ops = ERR_PTR(-ENOPROTOOPT);
  	} else {
  		ops = ERR_PTR(-ENOPROTOOPT);
  	}
  
  	mutex_unlock(&dsa_tag_drivers_lock);
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
104
105
106
  
  	return ops;
  }
4dad81ee1   Andrew Lunn   dsa: Add stub tag...
107
108
  void dsa_tag_driver_put(const struct dsa_device_ops *ops)
  {
367561753   Andrew Lunn   dsa: Make use of ...
109
110
111
112
113
114
115
116
117
118
  	struct dsa_tag_driver *dsa_tag_driver;
  
  	mutex_lock(&dsa_tag_drivers_lock);
  	list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) {
  		if (dsa_tag_driver->ops == ops) {
  			module_put(dsa_tag_driver->owner);
  			break;
  		}
  	}
  	mutex_unlock(&dsa_tag_drivers_lock);
4dad81ee1   Andrew Lunn   dsa: Add stub tag...
119
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  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 ...
137
  struct net_device *dsa_dev_to_net_device(struct device *dev)
91da11f87   Lennert Buytenhek   net: Distributed ...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  {
  	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 ...
154
  EXPORT_SYMBOL_GPL(dsa_dev_to_net_device);
91da11f87   Lennert Buytenhek   net: Distributed ...
155

90af1059c   Brandon Streiff   net: dsa: forward...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  /* Determine if we should defer delivery of skb until we have a rx timestamp.
   *
   * Called from dsa_switch_rcv. For now, this will only work if tagging is
   * enabled on the switch. Normally the MAC driver would retrieve the hardware
   * timestamp when it reads the packet out of the hardware. However in a DSA
   * switch, the DSA driver owning the interface to which the packet is
   * delivered is never notified unless we do so here.
   */
  static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p,
  				       struct sk_buff *skb)
  {
  	struct dsa_switch *ds = p->dp->ds;
  	unsigned int type;
  
  	if (skb_headroom(skb) < ETH_HLEN)
  		return false;
  
  	__skb_push(skb, ETH_HLEN);
  
  	type = ptp_classify_raw(skb);
  
  	__skb_pull(skb, ETH_HLEN);
  
  	if (type == PTP_CLASS_NONE)
  		return false;
  
  	if (likely(ds->ops->port_rxtstamp))
  		return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type);
  
  	return false;
  }
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
187
  static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
89e49506b   Florian Westphal   dsa: remove unuse...
188
  			  struct packet_type *pt, struct net_device *unused)
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
189
  {
2f657a600   Vivien Didelot   net: dsa: change ...
190
  	struct dsa_port *cpu_dp = dev->dsa_ptr;
a86d8becc   Florian Fainelli   net: dsa: Factor ...
191
  	struct sk_buff *nskb = NULL;
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
192
  	struct pcpu_sw_netstats *s;
f613ed665   Florian Fainelli   net: dsa: Add sup...
193
  	struct dsa_slave_priv *p;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
194

2f657a600   Vivien Didelot   net: dsa: change ...
195
  	if (unlikely(!cpu_dp)) {
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
196
197
198
  		kfree_skb(skb);
  		return 0;
  	}
16c5dcb13   Florian Fainelli   net: dsa: Move sk...
199
200
201
  	skb = skb_unshare(skb, GFP_ATOMIC);
  	if (!skb)
  		return 0;
2f657a600   Vivien Didelot   net: dsa: change ...
202
  	nskb = cpu_dp->rcv(skb, dev, pt);
a86d8becc   Florian Fainelli   net: dsa: Factor ...
203
204
205
206
207
208
  	if (!nskb) {
  		kfree_skb(skb);
  		return 0;
  	}
  
  	skb = nskb;
f613ed665   Florian Fainelli   net: dsa: Add sup...
209
  	p = netdev_priv(skb->dev);
a86d8becc   Florian Fainelli   net: dsa: Factor ...
210
211
212
  	skb_push(skb, ETH_HLEN);
  	skb->pkt_type = PACKET_HOST;
  	skb->protocol = eth_type_trans(skb, skb->dev);
1dc0408cd   Florian Fainelli   net: dsa: Call ds...
213
214
215
216
217
218
219
220
  	if (unlikely(cpu_dp->ds->untag_bridge_pvid)) {
  		nskb = dsa_untag_bridge_pvid(skb);
  		if (!nskb) {
  			kfree_skb(skb);
  			return 0;
  		}
  		skb = nskb;
  	}
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
221
222
223
224
225
  	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 ...
226

90af1059c   Brandon Streiff   net: dsa: forward...
227
228
  	if (dsa_skb_defer_rx_timestamp(p, skb))
  		return 0;
e131a5634   Alexander Lobakin   net: dsa: add GRO...
229
  	gro_cells_receive(&p->gcells, skb);
a86d8becc   Florian Fainelli   net: dsa: Factor ...
230
231
  
  	return 0;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
232
  }
ac2629a47   Florian Fainelli   net: dsa: Move ds...
233
  #ifdef CONFIG_PM_SLEEP
e7d53ad32   Vivien Didelot   net: dsa: unexpor...
234
235
  static bool dsa_is_port_initialized(struct dsa_switch *ds, int p)
  {
68bb8ea8a   Vivien Didelot   net: dsa: use dsa...
236
237
238
  	const struct dsa_port *dp = dsa_to_port(ds, p);
  
  	return dp->type == DSA_PORT_TYPE_USER && dp->slave;
e7d53ad32   Vivien Didelot   net: dsa: unexpor...
239
  }
ac2629a47   Florian Fainelli   net: dsa: Move ds...
240
241
242
243
244
245
246
247
  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;
68bb8ea8a   Vivien Didelot   net: dsa: use dsa...
248
  		ret = dsa_slave_suspend(dsa_to_port(ds, i)->slave);
ac2629a47   Florian Fainelli   net: dsa: Move ds...
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  		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;
68bb8ea8a   Vivien Didelot   net: dsa: use dsa...
274
  		ret = dsa_slave_resume(dsa_to_port(ds, i)->slave);
ac2629a47   Florian Fainelli   net: dsa: Move ds...
275
276
277
278
279
280
281
282
  		if (ret)
  			return ret;
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(dsa_switch_resume);
  #endif
61b7363ff   Florian Fainelli   net: dsa: make ds...
283
  static struct packet_type dsa_pack_type __read_mostly = {
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
284
285
286
  	.type	= cpu_to_be16(ETH_P_XDSA),
  	.func	= dsa_switch_rcv,
  };
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
287
288
289
290
291
292
  static struct workqueue_struct *dsa_owq;
  
  bool dsa_schedule_work(struct work_struct *work)
  {
  	return queue_work(dsa_owq, work);
  }
60724d4ba   Florian Fainelli   net: dsa: Add sup...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  static ATOMIC_NOTIFIER_HEAD(dsa_notif_chain);
  
  int register_dsa_notifier(struct notifier_block *nb)
  {
  	return atomic_notifier_chain_register(&dsa_notif_chain, nb);
  }
  EXPORT_SYMBOL_GPL(register_dsa_notifier);
  
  int unregister_dsa_notifier(struct notifier_block *nb)
  {
  	return atomic_notifier_chain_unregister(&dsa_notif_chain, nb);
  }
  EXPORT_SYMBOL_GPL(unregister_dsa_notifier);
  
  int call_dsa_notifiers(unsigned long val, struct net_device *dev,
  		       struct dsa_notifier_info *info)
  {
  	info->dev = dev;
  	return atomic_notifier_call_chain(&dsa_notif_chain, val, info);
  }
  EXPORT_SYMBOL_GPL(call_dsa_notifiers);
6b2975242   Andrew Lunn   net: dsa: Add sup...
314
315
316
  int dsa_devlink_param_get(struct devlink *dl, u32 id,
  			  struct devlink_param_gset_ctx *ctx)
  {
ccc3e6b01   Andrew Lunn   net: dsa: Add hel...
317
  	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
6b2975242   Andrew Lunn   net: dsa: Add sup...
318
319
320
321
322
323
324
325
326
327
328
  
  	if (!ds->ops->devlink_param_get)
  		return -EOPNOTSUPP;
  
  	return ds->ops->devlink_param_get(ds, id, ctx);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_param_get);
  
  int dsa_devlink_param_set(struct devlink *dl, u32 id,
  			  struct devlink_param_gset_ctx *ctx)
  {
ccc3e6b01   Andrew Lunn   net: dsa: Add hel...
329
  	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
6b2975242   Andrew Lunn   net: dsa: Add sup...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
  
  	if (!ds->ops->devlink_param_set)
  		return -EOPNOTSUPP;
  
  	return ds->ops->devlink_param_set(ds, id, ctx);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_param_set);
  
  int dsa_devlink_params_register(struct dsa_switch *ds,
  				const struct devlink_param *params,
  				size_t params_count)
  {
  	return devlink_params_register(ds->devlink, params, params_count);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_params_register);
  
  void dsa_devlink_params_unregister(struct dsa_switch *ds,
  				   const struct devlink_param *params,
  				   size_t params_count)
  {
  	devlink_params_unregister(ds->devlink, params, params_count);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_params_unregister);
5cd73fbd7   Andrew Lunn   net: dsa: Add sup...
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
  int dsa_devlink_resource_register(struct dsa_switch *ds,
  				  const char *resource_name,
  				  u64 resource_size,
  				  u64 resource_id,
  				  u64 parent_resource_id,
  				  const struct devlink_resource_size_params *size_params)
  {
  	return devlink_resource_register(ds->devlink, resource_name,
  					 resource_size, resource_id,
  					 parent_resource_id,
  					 size_params);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_resource_register);
  
  void dsa_devlink_resources_unregister(struct dsa_switch *ds)
  {
  	devlink_resources_unregister(ds->devlink, NULL);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_resources_unregister);
  
  void dsa_devlink_resource_occ_get_register(struct dsa_switch *ds,
  					   u64 resource_id,
  					   devlink_resource_occ_get_t *occ_get,
  					   void *occ_get_priv)
  {
  	return devlink_resource_occ_get_register(ds->devlink, resource_id,
  						 occ_get, occ_get_priv);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_register);
  
  void dsa_devlink_resource_occ_get_unregister(struct dsa_switch *ds,
  					     u64 resource_id)
  {
  	devlink_resource_occ_get_unregister(ds->devlink, resource_id);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_unregister);
97c82c231   Andrew Lunn   net: dsa: Add dev...
389
390
391
392
393
394
395
396
397
  struct devlink_region *
  dsa_devlink_region_create(struct dsa_switch *ds,
  			  const struct devlink_region_ops *ops,
  			  u32 region_max_snapshots, u64 region_size)
  {
  	return devlink_region_create(ds->devlink, ops, region_max_snapshots,
  				     region_size);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_region_create);
08156ba43   Andrew Lunn   net: dsa: Add dev...
398
399
400
401
402
403
404
405
406
407
408
409
410
  struct devlink_region *
  dsa_devlink_port_region_create(struct dsa_switch *ds,
  			       int port,
  			       const struct devlink_port_region_ops *ops,
  			       u32 region_max_snapshots, u64 region_size)
  {
  	struct dsa_port *dp = dsa_to_port(ds, port);
  
  	return devlink_port_region_create(&dp->devlink_port, ops,
  					  region_max_snapshots,
  					  region_size);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_port_region_create);
97c82c231   Andrew Lunn   net: dsa: Add dev...
411
412
413
414
415
  void dsa_devlink_region_destroy(struct devlink_region *region)
  {
  	devlink_region_destroy(region);
  }
  EXPORT_SYMBOL_GPL(dsa_devlink_region_destroy);
e1eea8112   Vladimir Oltean   net: dsa: introdu...
416
417
418
419
420
421
422
423
  struct dsa_port *dsa_port_from_netdev(struct net_device *netdev)
  {
  	if (!netdev || !dsa_slave_dev_check(netdev))
  		return ERR_PTR(-ENODEV);
  
  	return dsa_slave_to_port(netdev);
  }
  EXPORT_SYMBOL_GPL(dsa_port_from_netdev);
91da11f87   Lennert Buytenhek   net: Distributed ...
424
425
  static int __init dsa_init_module(void)
  {
7df899c36   Ben Hutchings   dsa: Combine core...
426
  	int rc;
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
427
428
429
430
  	dsa_owq = alloc_ordered_workqueue("dsa_ordered",
  					  WQ_MEM_RECLAIM);
  	if (!dsa_owq)
  		return -ENOMEM;
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
431
432
  	rc = dsa_slave_register_notifier();
  	if (rc)
68be93024   YueHaibing   net: dsa: Fix err...
433
  		goto register_notifier_fail;
b73adef67   Florian Fainelli   net: dsa: integra...
434

3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
435
  	dev_add_pack(&dsa_pack_type);
409065b06   Andrew Lunn   dsa: Register the...
436
437
  	dsa_tag_driver_register(&DSA_TAG_DRIVER_NAME(none_ops),
  				THIS_MODULE);
7df899c36   Ben Hutchings   dsa: Combine core...
438
  	return 0;
68be93024   YueHaibing   net: dsa: Fix err...
439

68be93024   YueHaibing   net: dsa: Fix err...
440
441
442
443
  register_notifier_fail:
  	destroy_workqueue(dsa_owq);
  
  	return rc;
91da11f87   Lennert Buytenhek   net: Distributed ...
444
445
446
447
448
  }
  module_init(dsa_init_module);
  
  static void __exit dsa_cleanup_module(void)
  {
409065b06   Andrew Lunn   dsa: Register the...
449
  	dsa_tag_driver_unregister(&DSA_TAG_DRIVER_NAME(none_ops));
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
450
  	dsa_slave_unregister_notifier();
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
451
  	dev_remove_pack(&dsa_pack_type);
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
452
  	destroy_workqueue(dsa_owq);
91da11f87   Lennert Buytenhek   net: Distributed ...
453
454
  }
  module_exit(dsa_cleanup_module);
577d6a7c3   Rusty Russell   module: fix missi...
455
  MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
91da11f87   Lennert Buytenhek   net: Distributed ...
456
457
458
  MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:dsa");